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