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