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