]> git.lyx.org Git - lyx.git/blob - lib/configure.py
944df6e69bab1aac477dad23ea9a4bcbe6f83f1f
[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'],
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 literate   nw      NoWeb                  N  "" "%%"    "document"
270 \Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"
271 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document"
272 \Format linuxdoc   sgml    LinuxDoc               x  "" "%%"    "document"
273 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document"
274 \Format text       txt    "Plain text"            a  "" "%%"    "document"
275 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
276 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
277 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
278 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
279  #
280     checkViewer('a BibTeX editor', ['sensible-editor', 'jabref', 'JabRef', \
281         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
282         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
283         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
284         'nedit', 'gedit', 'notepad'],
285         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
286     #
287     #checkProg('a Postscript interpreter', ['gs'],
288     #  rc_entry = [ r'\ps_command "%%"' ])
289     checkViewer('a Postscript previewer', ['kghostview', 'evince', 'gv', 'ghostview -swap'],
290         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
291 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
292     #
293     checkViewer('a PDF previewer', ['kpdf', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
294                     'gv', 'ghostview'],
295         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector"
296 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector"
297 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector"'''])
298     #
299     checkViewer('a DVI previewer', ['xdvi', 'kdvi'],
300         rc_entry = [r'\Format dvi        dvi     DVI                    D  "%%" ""      "document,vector"'])
301     if dtl_tools:
302         # Windows only: DraftDVI
303         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
304     #
305     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
306         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document"'])
307     #
308     checkViewer('Noteedit', ['noteedit'],
309         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
310     #
311     checkViewer('an OpenDocument viewer', ['oowriter'],
312         rc_entry = [r'\Format odt        odt     OpenDocument           "" "%%" "%%"    "document,vector"'])
313     #
314     # entried that do not need checkProg
315     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
316 \Format fax        ""      Fax                    "" "" ""      "document"
317 \Format lyx        lyx     LyX                    "" "" ""      ""
318 \Format lyx13x     lyx13  "LyX 1.3.x"             "" "" ""      "document"
319 \Format lyx14x     lyx14  "LyX 1.4.x"             "" "" ""      "document"
320 \Format lyx15x     lyx15  "LyX 1.5.x"             "" "" ""      "document"
321 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
322 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
323 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
324 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
325 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
326 \Format program    ""      Program                "" "" ""      ""
327 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
328 \Format rtf        rtf    "Rich Text Format"      "" "" ""      "document,vector"
329 \Format sxw        sxw    "OpenOffice.Org (sxw)"  ""  ""        ""      "document,vector"
330 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
331 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
332 \Format word       doc    "MS Word"               W  "" ""      "document,vector"
333 \Format wordhtml   html   "HTML (MS Word)"        "" ""        ""       "document"
334 ''')
335
336
337 def checkConverterEntries():
338     ''' Check all converters (\converter entries) '''
339     checkProg('the pdflatex program', ['pdflatex $$i'],
340         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex"' ])
341     
342     ''' If we're running LyX in-place then tex2lyx will be found in
343             ../src/tex2lyx. Add this directory to the PATH temporarily and
344             search for tex2lyx.
345             Use PATH to avoid any problems with paths-with-spaces.
346     '''
347     path_orig = os.environ["PATH"]
348     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
349         os.pathsep + path_orig
350
351     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx', 'tex2lyx' + version_suffix],
352         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
353 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
354
355     os.environ["PATH"] = path_orig
356
357     #
358     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
359         rc_entry = [ r'\converter literate   latex      "%%"    ""' ])
360     #
361     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
362         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
363         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
364     #
365     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
366         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
367     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
368     path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
369         '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
370         'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
371         rc_entry = [ r'\converter latex      html       "%%"    "needaux"' ])
372     if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
373       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
374     else:
375       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
376
377     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
378     path, htmlconv = checkProg('a LaTeX -> MS Word converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
379         "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
380         "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
381         rc_entry = [ r'\converter latex      wordhtml   "%%"    "needaux"' ])
382     if htmlconv.find('htlatex') >= 0:
383       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
384     #
385     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
386         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
387     #
388     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
389         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
390     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
391     # Both SuSE and debian have oolatex
392     checkProg('a LaTeX -> Open Document converter', [
393         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\'', \
394         'oolatex $$i', 'oolatex.sh $$i', \
395         '/usr/share/tex4ht/oolatex $$i'],
396         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
397     # On windows it is called latex2rt.exe
398     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
399         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
400     #
401     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
402         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
403     #
404     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
405         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
406     #
407     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
408         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
409     #
410     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
411         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
412     #
413     checkProg('a PS to EPS converter', ['ps2eps $$i'],
414         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
415     #
416     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
417         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
418     #
419     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
420         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
421     #
422     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
423         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
424     #
425     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
426         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
427     #
428     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
429         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
430     #
431     path, dvipng = checkProg('dvipng', ['dvipng'])
432     if dvipng == "dvipng":
433         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
434     else:
435         addToRC(r'\converter lyxpreview png        ""   ""')
436     #  
437     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i'],
438         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
439     #
440     checkProg('a FIG -> EPS/PPM converter', ['fig2dev'],
441         rc_entry = [
442             r'''\converter fig        eps        "fig2dev -L eps $$i $$o"       ""
443 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
444 \converter fig        png        "fig2dev -L png $$i $$o"       ""''',
445             ''])
446     #
447     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
448         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
449     #
450     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
451         rc_entry = [
452             r'''\converter tgif       eps        "tgif -stdout -print -color -eps $$i > $$o"    ""
453 \converter tgif       ppm        "tgif -stdout -print -color -ppm $$i > $$o"    ""
454 \converter tgif       png        "tgif -stdout -print -color -png $$i > $$o"    ""
455 \converter tgif       pdf        "tgif -stdout -print -color -pdf $$i > $$o"    ""''',
456             ''])
457     #
458     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
459         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
460     #
461     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
462         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
463     #
464     checkProg('an EPS -> PDF converter', ['epstopdf'],
465         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
466     #
467     # no agr -> pdf converter, since the pdf library used by gracebat is not
468     # free software and therefore not compiled in in many installations.
469     # Fortunately, this is not a big problem, because we will use epstopdf to
470     # convert from agr to pdf via eps without loss of quality.
471     checkProg('a Grace -> Image converter', ['gracebat'],
472         rc_entry = [
473             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
474 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
475 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
476 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
477             ''])
478     #
479     #
480     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
481     if (lilypond != ''):
482         version_string = cmdOutput("lilypond --version")
483         match = re.match('GNU LilyPond (\S+)', version_string)
484         if match:
485             version_number = match.groups()[0]
486             version = version_number.split('.')
487             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
488                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps $$i" ""
489 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
490                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
491                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf $$i"      ""')
492                 print '+  found LilyPond version %s.' % version_number
493             else:
494                 print '+  found LilyPond, but version %s is too old.' % version_number
495         else:
496             print '+  found LilyPond, but could not extract version number.'
497     #
498     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
499         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
500     #
501     # FIXME: no rc_entry? comment it out
502     # checkProg('Image converter', ['convert $$i $$o'])
503     #
504     # Entries that do not need checkProg
505     addToRC(r'''\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"  ""
506 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
507 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
508 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
509 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
510 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""
511 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
512 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
513 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
514 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
515 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
516 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
517 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
518 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
519 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
520 ''')
521
522
523 def checkLinuxDoc():
524     ''' Check linuxdoc '''
525     #
526     path, LINUXDOC = checkProg('SGML-tools 1.x (LinuxDoc)', ['sgml2lyx'],
527         rc_entry = [
528         r'''\converter linuxdoc   lyx        "sgml2lyx $$i"     ""
529 \converter linuxdoc   latex      "sgml2latex $$i"       ""
530 \converter linuxdoc   dvi        "sgml2latex -o dvi $$i"        ""
531 \converter linuxdoc   html       "sgml2html $$i"        ""''',
532         r'''\converter linuxdoc   lyx        "" ""
533 \converter linuxdoc   latex      ""     ""
534 \converter linuxdoc   dvi        ""     ""
535 \converter linuxdoc   html       ""     ""''' ])
536     if LINUXDOC != '':
537         return ('yes', 'true', '\\def\\haslinuxdoc{yes}')
538     else:
539         return ('no', 'false', '')
540
541
542 def checkDocBook():
543     ''' Check docbook '''
544     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook) or db2x scripts', ['sgmltools', 'db2dvi'],
545         rc_entry = [
546             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
547 \converter docbook    html       "sgmltools -b html $$i"        ""''',
548             r'''\converter docbook    dvi        "db2dvi $$i"   ""
549 \converter docbook    html       "db2html $$i"  ""''',
550             r'''\converter docbook    dvi        ""     ""
551 \converter docbook    html       ""     ""'''])
552     #
553     if DOCBOOK != '':
554         return ('yes', 'true', '\\def\\hasdocbook{yes}')
555     else:
556         return ('no', 'false', '')
557
558
559 def checkOtherEntries():
560     ''' entries other than Format and Converter '''
561     checkProg('a *roff formatter', ['groff', 'nroff'],
562         rc_entry = [
563             r'\plaintext_roff_command "groff -t -Tlatin1 $$FName"',
564             r'\plaintext_roff_command "tbl $$FName | nroff"',
565             r'\plaintext_roff_command ""' ])
566     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
567         rc_entry = [ r'\chktex_command "%%"' ])
568     checkProg('BibTeX', ['jbibtex', 'bibtex'],
569         rc_entry = [ r'\bibtex_command "%%"' ])
570     checkProg('a spellchecker', ['ispell'],
571         rc_entry = [ r'\spell_command "%%"' ])
572     ## FIXME: OCTAVE is not used anywhere
573     # path, OCTAVE = checkProg('Octave', ['octave'])
574     ## FIXME: MAPLE is not used anywhere
575     # path, MAPLE = checkProg('Maple', ['maple'])
576     checkProg('a spool command', ['lp', 'lpr'],
577         rc_entry = [
578             r'''\print_spool_printerprefix "-d "
579 \print_spool_command "lp"''',
580             r'''\print_spool_printerprefix "-P",
581 \print_spool_command "lpr"''',
582             ''])
583     # Add the rest of the entries (no checkProg is required)
584     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
585 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
586 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
587 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
588 ''')
589
590
591 def processLayoutFile(file, bool_docbook, bool_linuxdoc):
592     ''' process layout file and get a line of result
593         
594         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
595         
596         \DeclareLaTeXClass{article}
597         \DeclareLaTeXClass[scrbook]{book (koma-script)}
598         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
599
600         we expect output:
601         
602         "article" "article" "article" "false"
603         "scrbook" "scrbook" "book (koma-script)" "false"
604         "svjog" "svjour" "article (Springer - svjour/jog)" "false"
605     '''
606     classname = file.split(os.sep)[-1].split('.')[0]
607     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
608     p = re.compile(r'\Declare(LaTeX|DocBook|LinuxDoc)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
609     for line in open(file).readlines():
610         res = p.search(line)
611         if res != None:
612             (classtype, optAll, opt, opt1, desc) = res.groups()
613             avai = {'LaTeX':'false', 'DocBook':bool_docbook, 'LinuxDoc':bool_linuxdoc}[classtype]
614             if opt == None:
615                 opt = classname
616             return '"%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai)
617     print "Layout file without \DeclareXXClass line. "
618     sys.exit(2)
619
620     
621 def checkLatexConfig(check_config, bool_docbook, bool_linuxdoc):
622     ''' Explore the LaTeX configuration 
623         Return None (will be passed to sys.exit()) for success.
624     '''
625     print 'checking LaTeX configuration... ',
626     # if --without-latex-config is forced, or if there is no previous 
627     # version of textclass.lst, re-generate a default file.
628     if not os.path.isfile('textclass.lst') or not check_config:
629         # remove the files only if we want to regenerate
630         removeFiles(['textclass.lst', 'packages.lst'])
631         #
632         # Then, generate a default textclass.lst. In case configure.py
633         # fails, we still have something to start lyx.
634         print ' default values'
635         print '+checking list of textclasses... '
636         tx = open('textclass.lst', 'w')
637         tx.write('''
638 # This file declares layouts and their associated definition files
639 # (include dir. relative to the place where this file is).
640 # It contains only default values, since chkconfig.ltx could not be run
641 # for some reason. Run ./configure.py if you need to update it after a
642 # configuration change.
643 ''')
644         # build the list of available layout files and convert it to commands
645         # for chkconfig.ltx
646         foundClasses = []
647         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
648             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
649             # valid file?
650             if not os.path.isfile(file): 
651                 continue
652             # get stuff between /xxxx.layout .
653             classname = file.split(os.sep)[-1].split('.')[0]
654             #  tr ' -' '__'`
655             cleanclass = classname.replace(' ', '_')
656             cleanclass = cleanclass.replace('-', '_')
657             # make sure the same class is not considered twice
658             if foundClasses.count(cleanclass) == 0: # not found before
659                 foundClasses.append(cleanclass)
660                 tx.write(processLayoutFile(file, bool_docbook, bool_linuxdoc))
661         tx.close()
662         print '\tdone'
663     if not check_config:
664         return None
665     # the following will generate textclass.lst.tmp, and packages.lst.tmp
666     else:
667         print '\tauto'
668         removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
669             'chkconfig.classes', 'chklayouts.tex'])
670         rmcopy = False
671         if not os.path.isfile( 'chkconfig.ltx' ):
672             shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
673             rmcopy = True
674         writeToFile('wrap_chkconfig.ltx', '%s\n%s\n\\input{chkconfig.ltx}\n' \
675             % (linuxdoc_cmd, docbook_cmd) )
676         # Construct the list of classes to test for.
677         # build the list of available layout files and convert it to commands
678         # for chkconfig.ltx
679         p1 = re.compile(r'\Declare(LaTeX|DocBook)Class')
680         testclasses = list()
681         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
682             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
683             if not os.path.isfile(file):
684                 continue
685             classname = file.split(os.sep)[-1].split('.')[0]
686             for line in open(file).readlines():
687                 if p1.search(line) == None:
688                     continue
689                 if line[0] != '#':
690                     print "Wrong input layout file with line '" + line
691                     sys.exit(3)
692                 testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
693                 break
694         testclasses.sort()
695         cl = open('chklayouts.tex', 'w')
696         for line in testclasses:
697             cl.write(line + '\n')
698         cl.close()
699         #
700         # we have chklayouts.tex, then process it
701         fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
702         while True:
703             line = fout.readline()
704             if not line:
705                 break;
706             if re.match('^\+', line):
707                 print line,
708         # if the command succeeds, None will be returned
709         ret = fout.close()
710         #
711         # currently, values in chhkconfig are only used to set
712         # \font_encoding
713         values = {}
714         for line in open('chkconfig.vars').readlines():
715             key, val = re.sub('-', '_', line).split('=')
716             val = val.strip()
717             values[key] = val.strip("'")
718         # chk_fontenc may not exist 
719         try:
720             addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
721         except:
722             pass
723         if rmcopy:   # remove the copied file
724             removeFiles( [ 'chkconfig.ltx' ] )
725         # if configure successed, move textclass.lst.tmp to textclass.lst
726         # and packages.lst.tmp to packages.lst
727         if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
728             and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
729             shutil.move('textclass.lst.tmp', 'textclass.lst')
730             shutil.move('packages.lst.tmp', 'packages.lst')
731         return ret
732
733
734 def checkModulesConfig():
735   removeFiles(['lyxmodules.lst'])
736
737   print '+checking list of modules... '
738   tx = open('lyxmodules.lst', 'w')
739   tx.write('''## This file declares modules and their associated definition files.
740 ## It has been automatically generated by configure
741 ## Use "Options/Reconfigure" if you need to update it after a
742 ## configuration change. 
743 ''')
744   # build the list of available modules
745   foundClasses = []
746   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
747       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
748       # valid file?
749       print file
750       if not os.path.isfile(file): 
751           continue
752       tx.write(processModuleFile(file, bool_docbook, bool_linuxdoc))
753   tx.close()
754   print '\tdone'
755
756
757 def processModuleFile(file, bool_docbook, bool_linuxdoc):
758     ''' process module file and get a line of result
759
760         The top of a module file should look like this:
761           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
762           #BeginDescription
763           #...body of description...
764           #EndDescription
765           #Requires: [list of required modules]
766           #Excludes: [list of excluded modules]
767         The last two lines are optional
768         We expect output:
769           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes"
770     '''
771     p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
772     r = re.compile(r'#+\s*Requires: (.*)')
773     x = re.compile(r'#+\s*Excludes: (.*)')
774     b = re.compile(r'#+\s*DescriptionBegin\s*$')
775     e = re.compile(r'#+\s*DescriptionEnd\s*$')
776
777     modname = desc = pkgs = req = excl = ""
778     readingDescription = False
779     descLines = []
780     filename = file.split(os.sep)[-1]
781     filename = filename[:-7]
782
783     for line in open(file).readlines():
784       if readingDescription:
785         res = e.search(line)
786         if res != None:
787           readingDescription = False
788           desc = " ".join(descLines)
789           continue
790         descLines.append(line[1:].strip())
791         continue
792       res = b.search(line)
793       if res != None:
794         readingDescription = True
795         continue
796       res = p.search(line)
797       if res != None:
798           (pkgs, modname) = res.groups()
799           if pkgs == None:
800             pkgs = ""
801           else:
802             tmp = [s.strip() for s in pkgs.split(",")]
803             pkgs = ",".join(tmp)
804           continue
805       res = r.search(line)
806       if res != None:
807         req = res.group(1)
808         tmp = [s.strip() for s in req.split("|")]
809         req = "|".join(tmp)
810         continue
811       res = x.search(line)
812       if res != None:
813         excl = res.group(1)
814         tmp = [s.strip() for s in excl.split("|")]
815         excl = "|".join(tmp)
816         continue
817     if modname != "":
818         return '"%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl)
819     print "Module file without \DeclareLyXModule line. "
820     sys.exit(2)
821
822
823 def checkTeXAllowSpaces():
824     ''' Let's check whether spaces are allowed in TeX file names '''
825     tex_allows_spaces = 'false'
826     if lyx_check_config:
827         print "Checking whether TeX allows spaces in file names... ",
828         writeToFile('a b.tex', r'\message{working^^J}' )
829         if os.name == 'nt':
830             latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
831         else:
832             latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
833         if 'working' in latex_out:
834             print 'yes'
835             tex_allows_spaces = 'true'
836         else:
837             print 'no'
838             tex_allows_spaces = 'false'
839         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
840         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
841
842
843 def removeTempFiles():
844     # Final clean-up
845     if not lyx_keep_temps:
846         removeFiles(['chkconfig.vars',  \
847             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
848             'chklayouts.tex', 'missfont.log', 
849             'chklatex.ltx', 'chklatex.log'])
850
851
852 if __name__ == '__main__':
853     lyx_check_config = True
854     outfile = 'lyxrc.defaults'
855     rc_entries = ''
856     lyx_keep_temps = False
857     version_suffix = ''
858     logfile = 'configure.log'
859     ## Parse the command line
860     for op in sys.argv[1:]:   # default shell/for list is $*, the options
861         if op in [ '-help', '--help', '-h' ]:
862             print '''Usage: configure [options]
863 Options:
864     --help                   show this help lines
865     --keep-temps             keep temporary files (for debug. purposes)
866     --without-latex-config   do not run LaTeX to determine configuration
867     --with-version-suffix=suffix suffix of binary installed files
868 '''
869             sys.exit(0)
870         elif op == '--without-latex-config':
871             lyx_check_config = False
872         elif op == '--keep-temps':
873             lyx_keep_temps = True
874         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
875             version_suffix = op[22:]
876         else:
877             print "Unknown option", op
878             sys.exit(1)
879     #
880     # set up log file for stdout and stderr
881     log = open(logfile, 'w')
882     sys.stdout = Tee(sys.stdout, log)
883     sys.stderr = Tee(sys.stderr, log)
884     # check if we run from the right directory
885     srcdir = os.path.dirname(sys.argv[0])
886     if srcdir == '':
887         srcdir = '.'
888     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
889         print "configure: error: cannot find chkconfig.ltx script"
890         sys.exit(1)
891     setEnviron()
892     createDirectories()
893     windows_style_tex_paths = checkTeXPaths()
894     dtl_tools = checkDTLtools()
895     ## Write the first part of outfile
896     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
897 # script. It contains default settings that have been determined by
898 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
899 # want to customize LyX, use LyX' Preferences dialog or modify directly 
900 # the "preferences" file instead. Any setting in that file will
901 # override the values given here.
902 ''')
903     # check latex
904     LATEX = checkLatex(dtl_tools)
905     checkFormatEntries(dtl_tools)
906     checkConverterEntries()
907     (chk_linuxdoc, bool_linuxdoc, linuxdoc_cmd) = checkLinuxDoc()
908     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
909     checkTeXAllowSpaces()
910     if windows_style_tex_paths != '':
911         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
912     checkOtherEntries()
913     # --without-latex-config can disable lyx_check_config
914     ret = checkLatexConfig(lyx_check_config and LATEX != '',
915         bool_docbook, bool_linuxdoc)
916     checkModulesConfig() #lyx_check_config and LATEX != '')
917     removeTempFiles()
918     # The return error code can be 256. Because most systems expect an error code
919     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
920     # a None for success, 'ret is not None' is used to exit.
921     sys.exit(ret is not None)