]> git.lyx.org Git - lyx.git/blob - lib/configure.py
Amend 3058deb: Make convert_fonts robust
[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 from __future__ import print_function
12 import glob, logging, os, errno, re, shutil, subprocess, sys, stat
13
14 # set up logging
15 logging.basicConfig(level = logging.DEBUG,
16     format = '%(levelname)s: %(message)s', # ignore application name
17     filename = 'configure.log',
18     filemode = 'w')
19 #
20 # Add a handler to log to console
21 console = logging.StreamHandler()
22 console.setLevel(logging.INFO) # the console only print out general information
23 formatter = logging.Formatter('%(message)s') # only print out the message itself
24 console.setFormatter(formatter)
25 logger = logging.getLogger('LyX')
26 logger.addHandler(console)
27
28 def quoteIfSpace(name):
29     " utility function: quote name if it contains spaces "
30     if ' ' in name:
31         return '"' + name + '"'
32     else:
33         return name
34
35 def writeToFile(filename, lines, append = False):
36     " utility function: write or append lines to filename "
37     if append:
38         file = open(filename, 'a')
39     else:
40         file = open(filename, 'w')
41     file.write(lines)
42     file.close()
43
44
45 def addToRC(lines):
46     ''' utility function: shortcut for appending lines to outfile
47         add newline at the end of lines.
48     '''
49     if lines.strip() != '':
50         writeToFile(outfile, lines + '\n', append = True)
51         logger.debug('Add to RC:\n' + lines + '\n\n')
52
53
54 def removeFiles(filenames):
55     '''utility function: 'rm -f'
56         ignore errors when file does not exist, or is a directory.
57     '''
58     for file in filenames:
59         try:
60             os.remove(file)
61             logger.debug('Removing file %s' % file)
62         except OSError as e:
63             if e.errno == errno.ENOENT: # no such file or directory
64                 logger.debug('No need to remove file %s (it does not exists)' % file)
65             elif e.errno == errno.EISDIR: # is a directory
66                 logger.debug('Failed to remove file %s (it is a directory)' % file)
67             else:
68                 logger.debug('Failed to remove file %s' % file)
69             pass
70
71
72 def cmdOutput(cmd, async = False):
73     '''utility function: run a command and get its output as a string
74         cmd: command to run
75         async: if False, return whole output as a string, otherwise
76                return the stdout handle from which the output can be
77                read (the caller is then responsible for closing it)
78     '''
79     if os.name == 'nt':
80         b = False
81         if sys.version_info[0] < 3:
82             cmd = 'cmd /d /c pushd ' + shortPath(os.getcwdu()) + '&' + cmd
83         else:
84             cmd = 'cmd /d /c pushd ' + shortPath(os.getcwd()) + '&' + cmd
85     else:
86         b = True
87     pipe = subprocess.Popen(cmd, shell=b, close_fds=b, stdin=subprocess.PIPE,
88                             stdout=subprocess.PIPE, universal_newlines=True)
89     pipe.stdin.close()
90     if async:
91         return pipe.stdout
92     output = pipe.stdout.read()
93     pipe.stdout.close()
94     return output.strip()
95
96
97 def shortPath(path):
98     ''' On Windows, return the short version of "path" if possible '''
99     if os.name == 'nt':
100         from ctypes import windll, create_unicode_buffer
101         GetShortPathName = windll.kernel32.GetShortPathNameW
102         shortlen = GetShortPathName(path, 0, 0)
103         shortpath = create_unicode_buffer(shortlen)
104         if GetShortPathName(path, shortpath, shortlen):
105             return shortpath.value
106     return path
107
108
109 def setEnviron():
110     ''' I do not really know why this is useful, but we might as well keep it.
111         NLS nuisances.
112         Only set these to C if already set.  These must not be set unconditionally
113         because not all systems understand e.g. LANG=C (notably SCO).
114         Fixing LC_MESSAGES prevents Solaris sh from translating var values in set!
115         Non-C LC_CTYPE values break the ctype check.
116     '''
117     os.environ['LANG'] = os.getenv('LANG', 'C')
118     os.environ['LC'] = os.getenv('LC_ALL', 'C')
119     os.environ['LC_MESSAGE'] = os.getenv('LC_MESSAGE', 'C')
120     os.environ['LC_CTYPE'] = os.getenv('LC_CTYPE', 'C')
121
122
123 def copy_tree(src, dst, preserve_symlinks=False, level=0):
124     ''' Copy an entire directory tree 'src' to a new location 'dst'.
125
126     Code inspired from distutils.copy_tree.
127          Copying ignores non-regular files and the cache directory.
128     Pipes may be present as leftovers from LyX for lyx-server.
129
130     If 'preserve_symlinks' is true, symlinks will be
131     copied as symlinks (on platforms that support them!); otherwise
132     (the default), the destination of the symlink will be copied.
133     '''
134
135     if not os.path.isdir(src):
136         raise FileError("cannot copy tree '%s': not a directory" % src)
137     try:
138         names = os.listdir(src)
139     except os.error as oserror:
140         (errno, errstr) = oserror.args
141         raise FileError("error listing files in '%s': %s" % (src, errstr))
142
143     if not os.path.isdir(dst):
144         os.makedirs(dst)
145
146     outputs = []
147
148     for name in names:
149         src_name = os.path.join(src, name)
150         dst_name = os.path.join(dst, name)
151         if preserve_symlinks and os.path.islink(src_name):
152             link_dest = os.readlink(src_name)
153             os.symlink(link_dest, dst_name)
154             outputs.append(dst_name)
155         elif level == 0 and name == 'cache':
156             logger.info("Skip cache %s", src_name)
157         elif os.path.isdir(src_name):
158             outputs.extend(
159                 copy_tree(src_name, dst_name, preserve_symlinks, level=(level + 1)))
160         elif stat.S_ISREG(os.stat(src_name).st_mode) or os.path.islink(src_name):
161             shutil.copy2(src_name, dst_name)
162             outputs.append(dst_name)
163         else:
164             logger.info("Ignore non-regular file %s", src_name)
165
166     return outputs
167
168
169 def checkUpgrade():
170     ''' Check for upgrade from previous version '''
171     cwd = os.getcwd()
172     basename = os.path.basename( cwd )
173     lyxrc = os.path.join(cwd, outfile)
174     if not os.path.isfile( lyxrc ) and basename.endswith( version_suffix ) :
175         logger.info('Checking for upgrade from previous version.')
176         parent = os.path.dirname(cwd)
177         appname = basename[:(-len(version_suffix))]
178         for version in ['-2.3', '-2.2', '-2.1', '-2.0', '-1.6' ]:
179             logger.debug('Checking for upgrade from previous version ' + version)
180             previous = os.path.join(parent, appname + version)
181             logger.debug('previous = ' + previous)
182             if os.path.isdir( previous ):
183                 logger.info('Found directory "%s".', previous)
184                 copy_tree( previous, cwd, True )
185                 logger.info('Content copied from directory "%s".', previous)
186                 return
187
188
189 def createDirectories():
190     ''' Create the build directories if necessary '''
191     for dir in ['bind', 'clipart', 'doc', 'examples', 'images', 'kbd',
192         'layouts', 'scripts', 'templates', 'ui' ]:
193         if not os.path.isdir( dir ):
194             try:
195                 os.mkdir( dir)
196                 logger.debug('Create directory %s.' % dir)
197             except:
198                 logger.error('Failed to create directory %s.' % dir)
199                 sys.exit(1)
200
201
202 def checkTeXPaths():
203     ''' Determine the path-style needed by the TeX engine on Win32 (Cygwin) '''
204     windows_style_tex_paths = ''
205     if LATEX == '':
206         return windows_style_tex_paths
207     if os.name == 'nt' or sys.platform == 'cygwin':
208         from tempfile import mkstemp
209         fd, tmpfname = mkstemp(suffix='.ltx')
210         if os.name == 'nt':
211             encoding = sys.getfilesystemencoding()
212             if sys.version_info[0] < 3:
213                 inpname = shortPath(unicode(tmpfname, encoding)).replace('\\', '/')
214             else:
215                 inpname = shortPath(tmpfname).replace('\\', '/') 
216         else:
217             inpname = cmdOutput('cygpath -m ' + tmpfname)
218         logname = os.path.basename(re.sub("(?i).ltx", ".log", inpname))
219         inpname = inpname.replace('~', '\\string~')
220         os.write(fd, b'\\relax')
221         os.close(fd)
222         latex_out = cmdOutput(r'latex "\nonstopmode\input{%s}\makeatletter\@@end"'
223                               % inpname)
224         if 'Error' in latex_out:
225             latex_out = cmdOutput(r'latex "\nonstopmode\input{\"%s\"}\makeatletter\@@end"'
226                                   % inpname)
227         if 'Error' in latex_out:
228             logger.warning("configure: TeX engine needs posix-style paths in latex files")
229             windows_style_tex_paths = 'false'
230         else:
231             logger.info("configure: TeX engine needs windows-style paths in latex files")
232             windows_style_tex_paths = 'true'
233         removeFiles([tmpfname, logname, 'texput.log'])
234     return windows_style_tex_paths
235
236
237 ## Searching some useful programs
238 def checkProg(description, progs, rc_entry = [], path = [], not_found = ''):
239     '''
240         This function will search a program in $PATH plus given path
241         If found, return directory and program name (not the options).
242
243         description: description of the program
244
245         progs: check programs, for each prog, the first word is used
246             for searching but the whole string is used to replace
247             %% for a rc_entry. So, feel free to add '$$i' etc for programs.
248
249         path: additional paths (will be prepended to the program name)
250
251         rc_entry: entry to outfile, can be
252             1. emtpy: no rc entry will be added
253             2. one pattern: %% will be replaced by the first found program,
254                 or '' if no program is found.
255             3. several patterns for each prog and not_found. This is used
256                 when different programs have different usages. If you do not
257                 want not_found entry to be added to the RC file, you can specify
258                 an entry for each prog and use '' for the not_found entry.
259
260         not_found: the value that should be used instead of '' if no program
261             was found
262
263     '''
264     # one rc entry for each progs plus not_found entry
265     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
266         logger.error("rc entry should have one item or item "
267                      "for each prog and not_found.")
268         sys.exit(2)
269     logger.info('checking for ' + description + '...')
270     ## print '(' + ','.join(progs) + ')',
271     additional_path = path
272     path = os.environ["PATH"].split(os.pathsep) + additional_path
273     extlist = ['']
274     if "PATHEXT" in os.environ:
275         extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
276     global java, perl
277     unquoted_space = re.compile(r'''((?:[^ "']|"[^"]*"|'[^']*')+)''')
278     for idx in range(len(progs)):
279         # ac_prog may have options, ac_word is the command name
280         ac_prog = progs[idx].replace('"', '\\"')
281         ac_word = unquoted_space.split(progs[idx])[1::2][0].strip('"')
282         if (ac_word.endswith('.class') or ac_word.endswith('.jar')) and java == '':
283             continue
284         if ac_word.endswith('.pl') and perl == '':
285             continue
286         msg = '+checking for "' + ac_word + '"... '
287         for ac_dir in path:
288             if hasattr(os, "access") and not os.access(ac_dir, os.F_OK):
289                 continue
290             for ext in extlist:
291                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
292                     logger.info(msg + ' yes')
293                     # deal with java and perl
294                     if ac_word.endswith('.class'):
295                         ac_prog = ac_prog.replace(ac_word, r'%s \"%s\"'
296                                     % (java, os.path.join(ac_dir, ac_word[:-6])))
297                     elif ac_word.endswith('.jar'):
298                         ac_prog = ac_prog.replace(ac_word, r'%s -jar \"%s\"'
299                                     % (java, os.path.join(ac_dir, ac_word)))
300                     elif ac_word.endswith('.pl'):
301                         ac_prog = ac_prog.replace(ac_word, r'%s -w \"%s\"'
302                                     % (perl, os.path.join(ac_dir, ac_word)))
303                     elif ac_dir in additional_path:
304                         ac_prog = ac_prog.replace(ac_word, r'\"%s\"'
305                                     % (os.path.join(ac_dir, ac_word)))
306                     # write rc entries for this command
307                     if len(rc_entry) == 1:
308                         addToRC(rc_entry[0].replace('%%', ac_prog))
309                     elif len(rc_entry) > 1:
310                         addToRC(rc_entry[idx].replace('%%', ac_prog))
311                     return [ac_dir, ac_word]
312         # if not successful
313         logger.info(msg + ' no')
314     # write rc entries for 'not found'
315     if len(rc_entry) > 0:  # the last one.
316         addToRC(rc_entry[-1].replace('%%', not_found))
317     return ['', not_found]
318
319
320 def checkProgAlternatives(description, progs, rc_entry = [],
321                           alt_rc_entry = [], path = [], not_found = ''):
322     '''
323         The same as checkProg, but additionally, all found programs will be added
324         as alt_rc_entries
325     '''
326     # one rc entry for each progs plus not_found entry
327     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
328         logger.error("rc entry should have one item or item for each prog and not_found.")
329         sys.exit(2)
330     logger.info('checking for ' + description + '...')
331     ## print '(' + ','.join(progs) + ')',
332     additional_path = path
333     path = os.environ["PATH"].split(os.pathsep) + additional_path
334     extlist = ['']
335     if "PATHEXT" in os.environ:
336         extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
337     found_prime = False
338     real_ac_dir = ''
339     real_ac_word = not_found
340     global java, perl
341     for idx in range(len(progs)):
342         # ac_prog may have options, ac_word is the command name
343         ac_prog = progs[idx]
344         ac_word = ac_prog.split(' ')[0]
345         if (ac_word.endswith('.class') or ac_word.endswith('.jar')) and java == '':
346             continue
347         if ac_word.endswith('.pl') and perl == '':
348             continue
349         msg = '+checking for "' + ac_word + '"... '
350         found_alt = False
351         for ac_dir in path:
352             if hasattr(os, "access") and not os.access(ac_dir, os.F_OK):
353                 continue
354             for ext in extlist:
355                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
356                     logger.info(msg + ' yes')
357                     pr = re.compile(r'(\\\S+)(.*)$')
358                     m = None
359                     # deal with java and perl
360                     if ac_word.endswith('.class'):
361                         ac_prog = ac_prog.replace(ac_word, r'%s \"%s\"'
362                                     % (java, os.path.join(ac_dir, ac_word[:-6])))
363                     elif ac_word.endswith('.jar'):
364                         ac_prog = ac_prog.replace(ac_word, r'%s -jar \"%s\"'
365                                     % (java, os.path.join(ac_dir, ac_word)))
366                     elif ac_word.endswith('.pl'):
367                         ac_prog = ac_prog.replace(ac_word, r'%s -w \"%s\"'
368                                     % (perl, os.path.join(ac_dir, ac_word)))
369                     elif ac_dir in additional_path:
370                         ac_prog = ac_prog.replace(ac_word, r'\"%s\"'
371                                     % (os.path.join(ac_dir, ac_word)))
372                     # write rc entries for this command
373                     if found_prime == False:
374                         if len(rc_entry) == 1:
375                             addToRC(rc_entry[0].replace('%%', ac_prog))
376                         elif len(rc_entry) > 1:
377                             addToRC(rc_entry[idx].replace('%%', ac_prog))
378                         real_ac_dir = ac_dir
379                         real_ac_word = ac_word
380                         found_prime = True
381                     if len(alt_rc_entry) == 1:
382                         alt_rc = alt_rc_entry[0]
383                         if alt_rc == "":
384                             # if no explicit alt_rc is given, construct one
385                             m = pr.match(rc_entry[0])
386                             if m:
387                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
388                         addToRC(alt_rc.replace('%%', ac_prog))
389                     elif len(alt_rc_entry) > 1:
390                         alt_rc = alt_rc_entry[idx]
391                         if alt_rc == "":
392                             # if no explicit alt_rc is given, construct one
393                             m = pr.match(rc_entry[idx])
394                             if m:
395                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
396                         addToRC(alt_rc.replace('%%', ac_prog))
397                     found_alt = True
398                     break
399             if found_alt:
400                 break
401         if found_alt == False:
402             # if not successful
403             logger.info(msg + ' no')
404     if found_prime:
405         return [real_ac_dir, real_ac_word]
406     # write rc entries for 'not found'
407     if len(rc_entry) > 0:  # the last one.
408         addToRC(rc_entry[-1].replace('%%', not_found))
409     return ['', not_found]
410
411
412 def addAlternatives(rcs, alt_type):
413     '''
414         Returns a \\prog_alternatives string to be used as an alternative
415         rc entry.  alt_type can be a string or a list of strings.
416     '''
417     r = re.compile(r'\\Format (\S+).*$')
418     m = None
419     alt = ''
420     alt_token = '\\%s_alternatives '
421     if isinstance(alt_type, str):
422         alt_tokens = [alt_token % alt_type]
423     else:
424         alt_tokens = [alt_token % s for s in alt_type]
425     for idxx in range(len(rcs)):
426         if len(rcs) == 1:
427             m = r.match(rcs[0])
428             if m:
429                 alt = '\n'.join([s + m.group(1) + ' "%%"' for s in alt_tokens])
430         elif len(rcs) > 1:
431             m = r.match(rcs[idxx])
432             if m:
433                 if idxx > 0:
434                     alt += '\n'
435                 alt += '\n'.join([s + m.group(1) + ' "%%"' for s in alt_tokens])
436     return alt
437
438
439 def listAlternatives(progs, alt_type, rc_entry = []):
440     '''
441         Returns a list of \\prog_alternatives strings to be used as alternative
442         rc entries.  alt_type can be a string or a list of strings.
443     '''
444     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
445         logger.error("rc entry should have one item or item for each prog and not_found.")
446         sys.exit(2)
447     alt_rc_entry = []
448     for idx in range(len(progs)):
449         if len(rc_entry) == 1:
450             rcs = rc_entry[0].split('\n')
451             alt = addAlternatives(rcs, alt_type)
452             alt_rc_entry.insert(0, alt)
453         elif len(rc_entry) > 1:
454             rcs = rc_entry[idx].split('\n')
455             alt = addAlternatives(rcs, alt_type)
456             alt_rc_entry.insert(idx, alt)
457     return alt_rc_entry
458
459
460 def checkViewer(description, progs, rc_entry = [], path = []):
461     ''' The same as checkProgAlternatives, but for viewers '''
462     alt_rc_entry = listAlternatives(progs, 'viewer', rc_entry)
463     return checkProgAlternatives(description, progs, rc_entry,
464                                  alt_rc_entry, path, not_found = 'auto')
465
466
467 def checkEditor(description, progs, rc_entry = [], path = []):
468     ''' The same as checkProgAlternatives, but for editors '''
469     alt_rc_entry = listAlternatives(progs, 'editor', rc_entry)
470     return checkProgAlternatives(description, progs, rc_entry,
471                                  alt_rc_entry, path, not_found = 'auto')
472
473
474 def checkViewerNoRC(description, progs, rc_entry = [], path = []):
475     ''' The same as checkViewer, but do not add rc entry '''
476     alt_rc_entry = listAlternatives(progs, 'viewer', rc_entry)
477     rc_entry = []
478     return checkProgAlternatives(description, progs, rc_entry,
479                                  alt_rc_entry, path, not_found = 'auto')
480
481
482 def checkEditorNoRC(description, progs, rc_entry = [], path = []):
483     ''' The same as checkViewer, but do not add rc entry '''
484     alt_rc_entry = listAlternatives(progs, 'editor', rc_entry)
485     rc_entry = []
486     return checkProgAlternatives(description, progs, rc_entry,
487                                  alt_rc_entry, path, not_found = 'auto')
488
489
490 def checkViewerEditor(description, progs, rc_entry = [], path = []):
491     ''' The same as checkProgAlternatives, but for viewers and editors '''
492     alt_rc_entry = listAlternatives(progs, ['editor', 'viewer'], rc_entry)
493     return checkProgAlternatives(description, progs, rc_entry,
494                                  alt_rc_entry, path, not_found = 'auto')
495
496
497 def checkDTLtools():
498     ''' Check whether DTL tools are available (Windows only) '''
499     # Find programs! Returned path is not used now
500     if ((os.name == 'nt' or sys.platform == 'cygwin') and
501             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
502             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
503         dtl_tools = True
504     else:
505         dtl_tools = False
506     return dtl_tools
507
508 def checkInkscape():
509     ''' Check whether Inkscape is available and return the full path (Windows only) '''
510     ''' On Mac OS (darwin) a wrapper is used - therefore the version is checked '''
511     ''' The answer of the real inkscape is validated and a fake binary used if this fails '''
512     if sys.platform == 'darwin':
513         version_string = cmdOutput("inkscape --version")
514         if version_string.startswith('Inkscape'):
515             return 'inkscape'
516         else:
517             return 'inkscape-binary'
518     elif os.name != 'nt':
519         return 'inkscape'
520     if sys.version_info[0] < 3:
521         import _winreg as winreg
522     else:
523         import winreg
524     aReg = winreg.ConnectRegistry(None, winreg.HKEY_CLASSES_ROOT)
525     try:
526         aKey = winreg.OpenKey(aReg, r"inkscape.svg\DefaultIcon")
527         val = winreg.QueryValueEx(aKey, "")
528         return str(val[0]).split('"')[1]
529     except EnvironmentError:
530         try:
531             aKey = winreg.OpenKey(aReg, r"Applications\inkscape.exe\shell\open\command")
532             val = winreg.QueryValueEx(aKey, "")
533             return str(val[0]).split('"')[1]
534         except EnvironmentError:
535             return 'inkscape'
536
537 def checkLatex(dtl_tools):
538     ''' Check latex, return lyx_check_config '''
539     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'latex2e $$i'])
540     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
541     #-----------------------------------------------------------------
542     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
543     if PLATEX != '':
544         # check if PLATEX is pLaTeX2e
545         writeToFile('chklatex.ltx', r'\nonstopmode\makeatletter\@@end')
546         # run platex on chklatex.ltx and check result
547         if cmdOutput(PLATEX + ' chklatex.ltx').find('pLaTeX2e') != -1:
548             # We have the Japanese pLaTeX2e
549             addToRC(r'\converter platex   dvi       "%s"   "latex=platex"' % PLATEX)
550         else:
551             PLATEX = ''
552             removeFiles(['chklatex.ltx', 'chklatex.log'])
553     #-----------------------------------------------------------------
554     # use LATEX to convert from latex to dvi if PPLATEX is not available
555     if PPLATEX == '':
556         PPLATEX = LATEX
557     if dtl_tools:
558         # Windows only: DraftDVI
559         addToRC(r'''\converter latex      dvi2       "%s"       "latex"
560 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
561     else:
562         addToRC(r'\converter latex      dvi        "%s" "latex"' % PPLATEX)
563     # no latex
564     if LATEX != '':
565         # Check if latex is usable
566         writeToFile('chklatex.ltx', r'''
567 \nonstopmode
568 \ifx\undefined\documentclass\else
569   \message{ThisIsLaTeX2e}
570 \fi
571 \makeatletter
572 \@@end
573 ''')
574         # run latex on chklatex.ltx and check result
575         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
576             # valid latex2e
577             return LATEX
578         else:
579             logger.warning("Latex not usable (not LaTeX2e) ")
580         # remove temporary files
581         removeFiles(['chklatex.ltx', 'chklatex.log'])
582     return ''
583
584
585 def checkLuatex():
586     ''' Check if luatex is there '''
587     path, LUATEX = checkProg('LuaTeX', ['lualatex $$i'])
588     path, DVILUATEX = checkProg('LuaTeX (DVI)', ['dvilualatex $$i'])
589     if LUATEX != '':
590         addToRC(r'\converter luatex      pdf5       "%s"        "latex=lualatex"' % LUATEX)
591     if DVILUATEX != '':
592         addToRC(r'\converter dviluatex   dvi3        "%s"       "latex=dvilualatex"' % DVILUATEX)
593
594
595 def checkModule(module):
596     ''' Check for a Python module, return the status '''
597     msg = 'checking for "' + module + ' module"... '
598     try:
599       __import__(module)
600       logger.info(msg + ' yes')
601       return True
602     except ImportError:
603       logger.info(msg + ' no')
604       return False
605
606
607 texteditors = ['xemacs', 'gvim', 'kedit', 'kwrite', 'kate',
608                'nedit', 'gedit', 'geany', 'leafpad', 'mousepad',
609                'xed', 'notepad', 'WinEdt', 'WinShell', 'PSPad']
610
611 def checkFormatEntries(dtl_tools):
612     ''' Check all formats (\Format entries) '''
613     checkViewerEditor('a Tgif viewer and editor', ['tgif'],
614         rc_entry = [r'\Format tgif      "obj, tgo" Tgif                 "" "%%" "%%"    "vector"        "application/x-tgif"'])
615     #
616     checkViewerEditor('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
617         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"        "application/x-xfig"'])
618     #
619     checkViewerEditor('a Dia viewer and editor', ['dia'],
620         rc_entry = [r'\Format dia        dia     DIA                    "" "%%" "%%"    "vector,zipped=native", "application/x-dia-diagram"'])
621     #
622     checkViewerEditor('an OpenDocument drawing viewer and editor', ['libreoffice', 'lodraw', 'ooffice', 'oodraw', 'soffice'],
623         rc_entry = [r'\Format odg        "odg, sxd" "OpenDocument drawing"   "" "%%"    "%%"    "vector,zipped=native"  "application/vnd.oasis.opendocument.graphics"'])
624     #
625     checkViewerEditor('a Grace viewer and editor', ['xmgrace'],
626         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"        ""'])
627     #
628     checkViewerEditor('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
629         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""      ""'])
630     #
631     checkViewerEditor('a SVG viewer and editor', [inkscape_gui],
632         rc_entry = [r'''\Format svg        "svg" SVG                "" "%%" "%%"        "vector"        "image/svg+xml"
633 \Format svgz       "svgz" "SVG (compressed)" "" "%%" "%%"       "vector,zipped=native"  ""'''],
634         path = [inkscape_path])
635     #
636     imageformats = r'''\Format bmp        bmp     BMP                    "" "%s"        "%s"    ""      "image/x-bmp"
637 \Format gif        gif     GIF                    "" "%s"       "%s"    ""      "image/gif"
638 \Format jpg       "jpg, jpeg" JPEG                "" "%s"       "%s"    ""      "image/jpeg"
639 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""      "image/x-portable-bitmap"
640 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""      "image/x-portable-graymap"
641 \Format png        png     PNG                    "" "%s"       "%s"    ""      "image/x-png"
642 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""      "image/x-portable-pixmap"
643 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""      "image/tiff"
644 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""      "image/x-xbitmap"
645 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""      "image/x-xpixmap"'''
646     path, iv = checkViewerNoRC('a raster image viewer',
647         ['xv', 'gwenview', 'kview',
648          'eog', 'xviewer', 'ristretto', 'gpicview', 'lximage-qt',
649          'xdg-open', 'gimp-remote', 'gimp'],
650         rc_entry = [imageformats])
651     path, ie = checkEditorNoRC('a raster image editor',
652         ['gimp-remote', 'gimp'], rc_entry = [imageformats])
653     addToRC(imageformats % ((iv, ie)*10))
654     #
655     checkViewerEditor('a text editor', texteditors,
656         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""      ""
657 \Format docbook    sgml    DocBook                B  "" "%%"    "document,menu=export"  ""
658 \Format docbook-xml xml   "DocBook (XML)"         "" "" "%%"    "document,menu=export"  "application/docbook+xml"
659 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"        "text/vnd.graphviz"
660 \Format dviluatex  tex    "LaTeX (dviluatex)"     "" "" "%%"    "document,menu=export"  ""
661 \Format platex     tex    "LaTeX (pLaTeX)"        "" "" "%%"    "document,menu=export"  ""
662 \Format literate   nw      NoWeb                  N  "" "%%"    "document,menu=export"  ""
663 \Format sweave     Rnw    "Sweave"                S  "" "%%"    "document,menu=export"  ""
664 \Format sweave-ja  Rnw    "Sweave (Japanese)"     S  "" "%%"    "document,menu=export"  ""
665 \Format r          R      "R/S code"              "" "" "%%"    "document,menu=export"  ""
666 \Format knitr      Rnw    "Rnw (knitr)"           "" "" "%%"    "document,menu=export"  ""
667 \Format knitr-ja   Rnw    "Rnw (knitr, Japanese)" "" "" "%%"    "document,menu=export"  ""
668 \Format lilypond-book    lytex "LilyPond book (LaTeX)"   "" ""  "%%"    "document,menu=export"  ""
669 \Format lilypond-book-ja lytex "LilyPond book (pLaTeX)"   "" "" "%%"    "document,menu=export"  ""
670 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document,menu=export"  "text/x-tex"
671 \Format luatex     tex    "LaTeX (LuaTeX)"        "" "" "%%"    "document,menu=export"  ""
672 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document,menu=export"  ""
673 \Format xetex      tex    "LaTeX (XeTeX)"         "" "" "%%"    "document,menu=export"  ""
674 \Format latexclipboard tex "LaTeX (clipboard)"    "" "" "%%"    ""      ""
675 \Format text       txt    "Plain text"            a  "" "%%"    "document,menu=export"  "text/plain"
676 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"      ""
677 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"      ""
678 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"      ""
679 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"      ""
680 \Format beamer.info pdf.info   "Info (Beamer)"         "" ""   "%%"    "document,menu=export"   ""''' ])
681    #Lilypond files have special editors, but fall back to plain text editors
682     checkViewerEditor('a lilypond editor',
683         ['frescobaldi'] + texteditors,
684         rc_entry = [r'''\Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"        "text/x-lilypond"''' ])
685    #Spreadsheets using ssconvert from gnumeric
686     checkViewer('gnumeric spreadsheet software', ['gnumeric'],
687       rc_entry = [r'''\Format gnumeric gnumeric "Gnumeric spreadsheet" "" ""    "%%"   "document"       "application/x-gnumeric"
688 \Format excel      xls    "Excel spreadsheet"      "" "" "%%"    "document"     "application/vnd.ms-excel"
689 \Format excel2     xlsx   "MS Excel Office Open XML" "" "" "%%" "document"      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
690 \Format html_table html   "HTML Table (for spreadsheets)"      "" "" "%%"    "document" "text/html"
691 \Format oocalc     ods    "OpenDocument spreadsheet" "" "" "%%"    "document"   "application/vnd.oasis.opendocument.spreadsheet"'''])
692  #
693     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
694         rc_entry = [r'\Format xhtml      xhtml   "LyXHTML"              y "%%" ""    "document,menu=export"     "application/xhtml+xml"'])
695  #
696     checkEditor('a BibTeX editor', ['jabref', 'JabRef',
697         'pybliographic', 'bibdesk', 'gbib', 'kbib',
698         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex', 'TeXnicCenter'] +
699         texteditors,
700         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""      "text/x-bibtex"''' ])
701     #
702     #checkProg('a Postscript interpreter', ['gs'],
703     #  rc_entry = [ r'\ps_command "%%"' ])
704     checkViewer('a Postscript previewer',
705                 ['kghostview', 'okular', 'qpdfview --unique',
706                  'evince', 'xreader',
707                  'gv', 'ghostview -swap', 'gsview64', 'gsview32'],
708         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"        "image/x-eps"
709 \Format eps2       eps    "EPS (uncropped)"       "" "%%"       ""      "vector"        ""
710 \Format eps3       eps    "EPS (cropped)"         "" "%%"       ""      "document"      ""
711 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector,menu=export"   "application/postscript"'''])
712     # for xdg-open issues look here: http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg151818.html
713     # maybe use "bestApplication()" from https://github.com/jleclanche/python-mime
714     # the MIME type is set for pdf6, because that one needs to be autodetectable by libmime
715     checkViewer('a PDF previewer',
716                 ['pdfview', 'kpdf', 'okular', 'qpdfview --unique',
717                  'evince', 'xreader', 'kghostview', 'xpdf', 'SumatraPDF',
718                  'acrobat', 'acroread', 'mupdf',
719                  'gv', 'ghostview', 'AcroRd32', 'gsview64', 'gsview32'],
720         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector,menu=export"   ""
721 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector,menu=export"   ""
722 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector,menu=export"   ""
723 \Format pdf4       pdf    "PDF (XeTeX)"           X  "%%"       ""      "document,vector,menu=export"   ""
724 \Format pdf5       pdf    "PDF (LuaTeX)"          u  "%%"       ""      "document,vector,menu=export"   ""
725 \Format pdf6       pdf    "PDF (graphics)"        "" "%%"       ""      "vector"        "application/pdf"
726 \Format pdf7       pdf    "PDF (cropped)"         "" "%%"       ""      "document,vector"       ""
727 \Format pdf8       pdf    "PDF (lower resolution)"         "" "%%"      ""      "document,vector"       ""'''])
728     #
729     checkViewer('a DVI previewer', ['xdvi', 'kdvi', 'okular',
730                                     'evince', 'xreader',
731                                     'yap', 'dviout -Set=!m'],
732         rc_entry = [r'''\Format dvi        dvi     DVI                    D  "%%"       ""      "document,vector,menu=export"   "application/x-dvi"
733 \Format dvi3       dvi     "DVI (LuaTeX)"          V  "%%"      ""      "document,vector,menu=export"   ""'''])
734     if dtl_tools:
735         # Windows only: DraftDVI
736         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"        ""')
737     #
738     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
739         rc_entry = [r'\Format html      "html, htm" HTML                H  "%%" ""      "document,menu=export"  "text/html"'])
740     #
741     checkViewerEditor('Noteedit', ['noteedit'],
742         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"        ""'])
743     #
744     checkViewerEditor('an OpenDocument viewer', ['libreoffice', 'lwriter', 'lowriter', 'oowriter', 'swriter', 'abiword'],
745         rc_entry = [r'''\Format odt        odt     "OpenDocument (tex4ht)"  "" "%%"     "%%"    "document,vector,menu=export"   "application/vnd.oasis.opendocument.text"
746 \Format odt2       odt    "OpenDocument (eLyXer)"  "" "%%"      "%%"    "document,vector,menu=export"   "application/vnd.oasis.opendocument.text"
747 \Format odt3       odt    "OpenDocument (Pandoc)"  "" "%%"      "%%"    "document,vector,menu=export"   "application/vnd.oasis.opendocument.text"
748 \Format sxw        sxw    "OpenOffice.Org (sxw)"  "" "" ""      "document,vector"       "application/vnd.sun.xml.writer"'''])
749     #
750     checkViewerEditor('a Rich Text and Word viewer', ['libreoffice', 'lwriter', 'lowriter', 'oowriter', 'swriter', 'abiword'],
751         rc_entry = [r'''\Format rtf        rtf    "Rich Text Format"      "" "%%"       "%%"    "document,vector,menu=export"   "application/rtf"
752 \Format word       doc    "MS Word"               W  "%%"       "%%"    "document,vector,menu=export"   "application/msword"
753 \Format word2      docx    "MS Word Office Open XML"               O  "%%"      "%%"    "document,vector,menu=export"   "application/vnd.openxmlformats-officedocument.wordprocessingml.document"'''])
754     #
755     # entries that do not need checkProg
756     addToRC(r'''\Format csv        csv    "Table (CSV)"           "" "" ""      "document"      "text/csv"
757 \Format fax        ""      Fax                    "" "" ""      "document"      ""
758 \Format lyx        lyx     LyX                    "" "" ""      ""      "application/x-lyx"
759 \Format lyx13x     13.lyx "LyX 1.3.x"             "" "" ""      "document"      ""
760 \Format lyx14x     14.lyx "LyX 1.4.x"             "" "" ""      "document"      ""
761 \Format lyx15x     15.lyx "LyX 1.5.x"             "" "" ""      "document"      ""
762 \Format lyx16x     16.lyx "LyX 1.6.x"             "" "" ""      "document"      ""
763 \Format lyx20x     20.lyx "LyX 2.0.x"             "" "" ""      "document"      ""
764 \Format lyx21x     21.lyx "LyX 2.1.x"             "" "" ""      "document"      ""
765 \Format lyx22x     22.lyx "LyX 2.2.x"             "" "" ""      "document"      ""
766 \Format lyx23x     23.lyx "LyX 2.3.x"             "" "" ""      "document,menu=export"  ""
767 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"      ""
768 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"      ""
769 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"      ""
770 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""      ""
771 \Format pdftex     "pdftex_t, pdf_tex" PDFTEX                "" ""      ""      ""      ""
772 \Format program    ""      Program                "" "" ""      ""      ""
773 \Format pstex      "pstex_t, ps_tex" PSTEX                  "" ""       ""      ""      ""
774 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"        "image/x-wmf"
775 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"        "image/x-emf"
776 \Format wordhtml  "html, htm" "HTML (MS Word)"    "" "" ""      "document"      ""
777 ''')
778
779
780 def checkConverterEntries():
781     ''' Check all converters (\converter entries) '''
782     checkProg('the pdflatex program', ['pdflatex $$i'],
783         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex=pdflatex,hyperref-driver=pdftex"' ])
784
785     checkProg('XeTeX', ['xelatex $$i'],
786         rc_entry = [ r'\converter xetex      pdf4       "%%"    "latex=xelatex,hyperref-driver=xetex"' ])
787
788     checkLuatex()
789
790     # Look for tex2lyx in this order (see bugs #3308 and #6986):
791     #   1)  If we're building LyX with autotools then tex2lyx is found
792     #       in the subdirectory tex2lyx with respect to the binary dir.
793     #   2)  If we're building LyX with cmake then tex2lyx is found
794     #       in the binary dir.
795     #   3)  If LyX was configured with a version suffix then tex2lyx
796     #       will also have this version suffix.
797     #   4)  Otherwise always use tex2lyx.
798     in_binary_subdir = os.path.join(lyx_binary_dir, 'tex2lyx', 'tex2lyx')
799     in_binary_subdir = os.path.abspath(in_binary_subdir).replace('\\', '/')
800
801     in_binary_dir = os.path.join(lyx_binary_dir, 'tex2lyx')
802     in_binary_dir = os.path.abspath(in_binary_dir).replace('\\', '/')
803
804     path, t2l = checkProg('a LaTeX/Noweb -> LyX converter', [quoteIfSpace(in_binary_subdir), quoteIfSpace(in_binary_subdir + version_suffix), quoteIfSpace(in_binary_dir), quoteIfSpace(in_binary_dir + version_suffix), 'tex2lyx' + version_suffix, 'tex2lyx'],
805         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
806 \converter latexclipboard lyx        "%% -fixedenc utf8 -f $$i $$o"     ""
807 \converter literate   lyx        "%% -n -m noweb -f $$i $$o"    ""
808 \converter sweave   lyx        "%% -n -m sweave -f $$i $$o"     "needauth"
809 \converter knitr   lyx        "%% -n -m knitr -f $$i $$o"       "needauth"'''], not_found = 'tex2lyx')
810     if path == '':
811         logger.warning("Failed to find tex2lyx on your system.")
812
813     #
814     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
815         rc_entry = [r'''\converter literate   latex      "%%"   ""
816 \converter literate   pdflatex      "%%"        ""
817 \converter literate   xetex         "%%"        ""
818 \converter literate   luatex        "%%"        ""
819 \converter literate   dviluatex     "%%"        ""'''])
820     #
821     checkProg('a Sweave -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxsweave.R $$p$$i $$p$$o $$e $$r'],
822         rc_entry = [r'''\converter sweave   latex      "%%"     "needauth"
823 \converter sweave   pdflatex   "%%"     "needauth"
824 \converter sweave-ja   platex     "%%"  "needauth"
825 \converter sweave   xetex      "%%"     "needauth"
826 \converter sweave   luatex     "%%"     "needauth"
827 \converter sweave   dviluatex  "%%"     "needauth"'''])
828     #
829     checkProg('a knitr -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r'],
830         rc_entry = [r'''\converter knitr   latex      "%%"      "needauth"
831 \converter knitr   pdflatex   "%%"      "needauth"
832 \converter knitr-ja   platex     "%%"   "needauth"
833 \converter knitr   xetex      "%%"      "needauth"
834 \converter knitr   luatex     "%%"      "needauth"
835 \converter knitr   dviluatex  "%%"      "needauth"'''])
836     #
837     checkProg('a Sweave -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxstangle.R $$i $$e $$r'],
838         rc_entry = [ r'\converter sweave      r      "%%"    ""',
839                      r'\converter sweave-ja   r      "%%"    ""' ])
840     #
841     checkProg('a knitr -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r tangle'],
842         rc_entry = [ r'\converter knitr      r      "%%"    ""',
843                      r'\converter knitr-ja   r      "%%"    ""' ])
844     #
845     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex',
846         'htmltolatex -input $$i -output $$o', 'htmltolatex.jar -input $$i -output $$o'],
847         rc_entry = [ r'\converter html       latex      "%%"    ""',
848                      r'\converter html       latex      "python -tt $$s/scripts/html2latexwrapper.py %% $$i $$o"        ""',
849                      r'\converter html       latex      "%%"    ""',
850                      r'\converter html       latex      "%%"    ""', '' ])
851     #
852     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
853         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
854
855     # eLyXer: search as an executable (elyxer.py, elyxer)
856     path, elyxer = checkProg('a LyX -> HTML converter',
857         ['elyxer.py --nofooter --directory $$r $$i $$o', 'elyxer --nofooter --directory $$r $$i $$o'],
858         rc_entry = [ r'\converter lyx      html       "%%"      ""' ])
859     path, elyxer = checkProg('a LyX -> HTML (MS Word) converter',
860         ['elyxer.py --nofooter --html --directory $$r $$i $$o', 'elyxer --nofooter --html --directory $$r $$i $$o'],
861         rc_entry = [ r'\converter lyx      wordhtml       "%%"  ""' ])
862     path, elyxer = checkProg('a LyX -> OpenDocument (eLyXer) converter',
863         ['elyxer.py --html --nofooter --unicode --directory $$r $$i $$o', 'elyxer --html --nofooter --unicode --directory $$r $$i $$o'],
864         rc_entry = [ r'\converter lyx      odt2       "%%"      ""' ])
865     path, elyxer = checkProg('a LyX -> Word converter',
866         ['elyxer.py --html --nofooter --unicode --directory $$r $$i $$o', 'elyxer --html --nofooter --unicode --directory $$r $$i $$o'],
867         rc_entry = [ r'\converter lyx      word      "%%"       ""' ])
868     if elyxer.find('elyxer') >= 0:
869       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
870       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
871     else:
872       # search for HTML converters other than eLyXer
873       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
874       path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i',
875           '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o',
876           'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
877           rc_entry = [ r'\converter latex      html       "%%"  "needaux"' ])
878       if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
879         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
880       else:
881         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
882       path, htmlconv = checkProg('a LaTeX -> HTML (MS Word) converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'",
883           "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'",
884           "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
885           rc_entry = [ r'\converter latex      wordhtml   "%%"  "needaux"' ])
886       if htmlconv.find('htlatex') >= 0:
887         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
888       else:
889         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
890
891
892     # Check if LyXBlogger is installed
893     lyxblogger_found = checkModule('lyxblogger')
894     if lyxblogger_found:
895       addToRC(r'\Format    blog       blog       "LyXBlogger"           "" "" ""  "document"  ""')
896       addToRC(r'\converter xhtml      blog       "python -m lyxblogger $$i"       ""')
897
898     #
899     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
900         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
901     #
902     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
903         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
904     #
905     checkProg('an Open Document (Pandoc) -> LaTeX converter', ['pandoc -s -f odt -o $$o -t latex $$i'],
906         rc_entry = [ r'\converter odt3        latex      "%%"   ""' ])
907     #
908     checkProg('a MS Word Office Open XML converter -> LaTeX', ['pandoc -s -f docx -o $$o -t latex $$i'],
909         rc_entry = [ r'\converter word2      latex      "%%"    ""' ])
910     # Only define a converter to pdf6, otherwise the odt format could be
911     # used as an intermediate step for export to pdf, which is not wanted.
912     checkProg('an OpenDocument -> PDF converter', ['unoconv -f pdf --stdout $$i > $$o'],
913         rc_entry = [ r'\converter odt        pdf6       "%%"    ""' ])
914     # According to http://www.tug.org/applications/tex4ht/mn-commands.html
915     # the command mk4ht oolatex $$i has to be used as default,
916     # but as this would require to have Perl installed, in MiKTeX oolatex is
917     # directly available as application.
918     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
919     # Both SuSE and debian have oolatex
920     checkProg('a LaTeX -> Open Document (tex4ht) converter', [
921         'oolatex $$i', 'mk4ht oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i',
922         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\''],
923         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
924     # On windows it is called latex2rt.exe
925     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
926         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
927     #
928     checkProg('a LaTeX -> Open Document (Pandoc) converter', ['pandoc -s -f latex -o $$o -t odt $$i'],
929         rc_entry = [ r'\converter latex      odt3        "%%"   ""' ])
930     #
931     checkProg('a LaTeX -> MS Word Office Open XML converter', ['pandoc -s -f latex -o $$o -t docx $$i'],
932         rc_entry = [ r'\converter latex      word2       "%%"   ""' ])
933     #
934     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
935         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
936     # Do not define a converter to pdf6, ps is a pure export format
937     checkProg('a PS to PDF converter', ['ps2pdf $$i $$o'],
938         rc_entry = [ r'\converter ps         pdf        "%%"    "hyperref-driver=dvips"' ])
939     #
940     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
941         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
942     #
943     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
944         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
945     # Need to call ps2eps in a pipe, otherwise it would name the output file
946     # depending on the extension of the input file. We do not know the input
947     # file extension in general, so the resultfile= flag would not help.
948     # Since ps2eps crops the image, we do not use it to convert from ps->eps.
949     # This would create additional paths in the converter graph with unwanted
950     # side effects (e.g. ps->pdf via ps2pdf would create a different result
951     # than ps->eps->pdf via ps2eps and epstopdf).
952     checkProg('a PS to EPS converter', ['ps2eps -- < $$i > $$o'],
953         rc_entry = [ r'\converter eps2       eps      "%%"      ""' ])
954     #
955     checkProg('a PDF to PS converter', ['pdftops $$i $$o', 'pdf2ps $$i $$o'],
956         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
957     # Only define a converter from pdf6 for graphics
958     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
959         rc_entry = [ r'\converter pdf6        eps        "%%"   ""' ])
960     # Define a converter from pdf6 to png for Macs where pdftops is missing.
961     # The converter utility sips allows to force the dimensions of the resulting
962     # png image. The value of 800 pixel for the width is arbitrary and not
963     # related to the current screen resolution or width.
964     # There is no converter parameter for this information.
965     checkProg('a PDF to PNG converter',
966         ['sips --resampleWidth 800 --setProperty format png $$i --out $$o'],
967         rc_entry = [ r'\converter pdf6        png        "%%" ""' ])
968     # Create one converter for a PDF produced using TeX fonts and one for a
969     # PDF produced using non-TeX fonts. This does not produce non-unique
970     # conversion paths, since a given document either uses TeX fonts or not.
971     checkProg('a PDF cropping tool', ['pdfcrop $$i $$o'],
972         rc_entry = [ r'''\converter pdf2   pdf7       "%%"      ""
973 \converter pdf4   pdf7       "%%"       ""''' ])
974     # Create one converter for a PDF produced using TeX fonts and one for a
975     # PDF produced using non-TeX fonts. This does not produce non-unique
976     # conversion paths, since a given document either uses TeX fonts or not.
977     checkProg('Ghostscript', ["gswin32c", "gswin64c", "gs"],
978         rc_entry = [ r'''\converter pdf2   pdf8       "python -tt $$s/scripts/convert_pdf.py $$i $$o ebook"     ""
979 \converter pdf4   pdf8       "python -tt $$s/scripts/convert_pdf.py $$i $$o ebook"      ""''' ])
980     #
981     checkProg('a Beamer info extractor', ['makebeamerinfo -p $$i'],
982         rc_entry = [ r'\converter pdf2         beamer.info        "%%"  ""' ])
983     #
984     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
985         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
986     #
987     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
988         rc_entry = [ r'\converter dvi        ps         "%%"    "hyperref-driver=dvips"' ])
989     #
990     checkProg('a DVI to cropped EPS converter', ['dvips -E -o $$o $$i'],
991         rc_entry = [ r'\converter dvi        eps3         "%%"  ""' ])
992     #
993     checkProg('a DVI to PDF converter', ['dvipdfmx', 'dvipdfm'],
994         rc_entry = [ r'\converter dvi        pdf3       "%%  -o $$o $$i"        "hyperref-driver=%%"' ])
995     #
996     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
997         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
998     #
999     path, fig2dev = checkProg('a FIG -> Image converter', ['fig2dev'])
1000     if fig2dev == "fig2dev":
1001         addToRC(r'''\converter fig        eps        "fig2dev -L eps $$i $$o"   ""
1002 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
1003 \converter fig        svg        "fig2dev -L svg $$i $$o"       ""
1004 \converter fig        png        "fig2dev -L png $$i $$o"       ""
1005 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
1006 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""''')
1007     #
1008     checkProg('a SVG -> PDFTeX converter', [inkscape_cl],
1009         rc_entry = [ r'\converter svg        pdftex     "python -tt $$s/scripts/svg2pdftex.py %% $$p$$i $$p$$o" ""'],
1010         path = [inkscape_path])
1011     #
1012     checkProg('a SVG -> PSTeX converter', [inkscape_cl],
1013         rc_entry = [ r'\converter svg        pstex     "python -tt $$s/scripts/svg2pstex.py %% $$p$$i $$p$$o" ""'],
1014         path = [inkscape_path])
1015     #
1016     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
1017         rc_entry = [ r'\converter tiff       eps        "%%"    ""'])
1018     #
1019     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
1020         rc_entry = [
1021             r'''\converter tgif       eps        "tgif -print -color -eps -stdout $$i > $$o"    ""
1022 \converter tgif       png        "tgif -print -color -png -o $$d $$i"   ""
1023 \converter tgif       pdf6       "tgif -print -color -pdf -stdout $$i > $$o"    ""'''])
1024     #
1025     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i', inkscape_cl + ' --file=%s$$i --export-area-drawing --without-gui --export-eps=%s$$o'
1026                % (inkscape_fileprefix, inkscape_fileprefix)],
1027         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
1028     #
1029     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', inkscape_cl + ' --file=%s$$i --export-area-drawing --without-gui --export-eps=%s$$o'
1030                % (inkscape_fileprefix, inkscape_fileprefix)],
1031         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
1032     #
1033     checkProg('a WMF -> PDF converter', [inkscape_cl + ' --file=%s$$i --export-area-drawing --without-gui --export-pdf=%s$$o' % (inkscape_fileprefix, inkscape_fileprefix)],
1034         rc_entry = [ r'\converter wmf        pdf6        "%%"   ""'])
1035     #
1036     checkProg('an EMF -> PDF converter', [inkscape_cl + ' --file=%s$$i --export-area-drawing --without-gui --export-pdf=%s$$o' % (inkscape_fileprefix, inkscape_fileprefix)],
1037         rc_entry = [ r'\converter emf        pdf6        "%%"   ""'])
1038     # Only define a converter to pdf6 for graphics
1039     checkProg('an EPS -> PDF converter', ['epstopdf'],
1040         rc_entry = [ r'\converter eps        pdf6       "epstopdf --outfile=$$o $$i"    ""'])
1041     #
1042     checkProg('an EPS -> PNG converter', ['magick $$i $$o', 'convert $$i $$o'],
1043         rc_entry = [ r'\converter eps        png        "%%"    ""'])
1044     #
1045     # no agr -> pdf6 converter, since the pdf library used by gracebat is not
1046     # free software and therefore not compiled in in many installations.
1047     # Fortunately, this is not a big problem, because we will use epstopdf to
1048     # convert from agr to pdf6 via eps without loss of quality.
1049     checkProg('a Grace -> Image converter', ['gracebat'],
1050         rc_entry = [
1051             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
1052 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
1053 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
1054 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""'''])
1055     #
1056     checkProg('a Dot -> Image converter', ['dot'],
1057         rc_entry = [
1058             r'''\converter dot        eps        "dot -Teps $$i -o $$o" ""
1059 \converter dot        png        "dot -Tpng $$i -o $$o" ""'''])
1060     #
1061     path, dia = checkProg('a Dia -> Image converter', ['dia'])
1062     if dia == 'dia':
1063         addToRC(r'''\converter dia        png        "dia -e $$o -t png $$i"    ""
1064 \converter dia        eps        "dia -e $$o -t eps $$i"        ""
1065 \converter dia        svg        "dia -e $$o -t svg $$i"        ""''')
1066
1067     #
1068     # Actually, this produces EPS, but with a wrong bounding box (usually A4 or letter).
1069     # The eps2->eps converter then fixes the bounding box by cropping.
1070     # Although unoconv can convert to png and pdf as well, do not define
1071     # odg->png and odg->pdf converters, since the bb would be too large as well.
1072     checkProg('an OpenDocument -> EPS converter', ['libreoffice --headless --nologo --convert-to eps $$i', 'unoconv -f eps --stdout $$i > $$o'],
1073         rc_entry = [ r'\converter odg        eps2       "%%"    ""'])
1074     #
1075     checkProg('a SVG (compressed) -> SVG converter', ['gunzip -c $$i > $$o'],
1076         rc_entry = [ r'\converter svgz       svg        "%%"    ""'])
1077     #
1078     checkProg('a SVG -> SVG (compressed) converter', ['gzip -c $$i > $$o'],
1079         rc_entry = [ r'\converter svg        svgz       "%%"    ""'])
1080     # Only define a converter to pdf6 for graphics
1081     # Prefer rsvg-convert over inkscape since it is faster (see http://www.lyx.org/trac/ticket/9891)
1082     checkProg('a SVG -> PDF converter', ['rsvg-convert -f pdf -o $$o $$i', inkscape_cl + ' --file=%s$$i --export-area-drawing --without-gui --export-pdf=%s$$o'
1083                % (inkscape_fileprefix, inkscape_fileprefix)],
1084         rc_entry = [ r'''\converter svg        pdf6       "%%"    ""
1085 \converter svgz       pdf6       "%%"    ""'''],
1086         path = ['', inkscape_path])
1087     #
1088     checkProg('a SVG -> EPS converter', ['rsvg-convert -f ps -o $$o $$i', inkscape_cl + ' --file=%s$$i --export-area-drawing --without-gui --export-eps=%s$$o'
1089                % (inkscape_fileprefix, inkscape_fileprefix)],
1090         rc_entry = [ r'''\converter svg        eps        "%%"    ""
1091 \converter svgz       eps        "%%"    ""'''],
1092         path = ['', inkscape_path])
1093     #
1094     checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', inkscape_cl + ' --without-gui --file=%s$$i --export-png=%s$$o'
1095                % (inkscape_fileprefix, inkscape_fileprefix)],
1096         rc_entry = [ r'''\converter svg        png        "%%"    "",
1097 \converter svgz       png        "%%"    ""'''],
1098         path = ['', inkscape_path])
1099     #
1100     checkProg('Gnuplot', ['gnuplot'], 
1101         rc_entry = [ r'''\Format gnuplot     "gp, gnuplot"    "Gnuplot"     "" "" ""  "vector"  "text/plain"
1102 \converter gnuplot      pdf6      "python -tt $$s/scripts/gnuplot2pdf.py $$i $$o"    "needauth"''' ])
1103     #
1104     # gnumeric/xls/ods to tex
1105     checkProg('a spreadsheet -> latex converter', ['ssconvert'],
1106        rc_entry = [ r'''\converter gnumeric latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1107 \converter oocalc latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1108 \converter excel  latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1109 \converter excel2 latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1110 \converter gnumeric html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1111 \converter oocalc html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1112 \converter excel  html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1113 \converter excel2 html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1114 '''])
1115
1116     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
1117     if (lilypond != ''):
1118         version_string = cmdOutput("lilypond --version")
1119         match = re.match('GNU LilyPond (\S+)', version_string)
1120         if match:
1121             version_number = match.groups()[0]
1122             version = version_number.split('.')
1123             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
1124                 addToRC(r'''\converter lilypond   eps        "lilypond -dbackend=eps -dsafe --ps $$i"   ""
1125 \converter lilypond   png        "lilypond -dbackend=eps -dsafe --png $$i"      ""''')
1126                 addToRC(r'\converter lilypond   pdf6       "lilypond -dbackend=eps -dsafe --pdf $$i"    ""')
1127                 logger.info('+  found LilyPond version %s.' % version_number)
1128             elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
1129                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps --safe $$i"  ""
1130 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
1131                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
1132                     addToRC(r'\converter lilypond   pdf6       "lilypond -b eps --pdf --safe $$i"       ""')
1133                 logger.info('+  found LilyPond version %s.' % version_number)
1134             else:
1135                 logger.info('+  found LilyPond, but version %s is too old.' % version_number)
1136         else:
1137             logger.info('+  found LilyPond, but could not extract version number.')
1138     #
1139     path, lilypond_book = checkProg('a LilyPond book (LaTeX) -> LaTeX converter', ['lilypond-book'])
1140     if (lilypond_book != ''):
1141         version_string = cmdOutput("lilypond-book --version")
1142         match = re.match('(\S+)$', version_string)
1143         if match:
1144             version_number = match.groups()[0]
1145             version = version_number.split('.')
1146             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 13):
1147                 # Note: The --lily-output-dir flag is required because lilypond-book
1148                 #       does not process input again unless the input has changed,
1149                 #       even if the output format being requested is different. So
1150                 #       once a .eps file exists, lilypond-book won't create a .pdf
1151                 #       even when requested with --pdf. This is a problem if a user
1152                 #       clicks View PDF after having done a View DVI. To circumvent
1153                 #       this, use different output folders for eps and pdf outputs.
1154                 addToRC(r'\converter lilypond-book latex    "lilypond-book --safe --lily-output-dir=ly-eps $$i"                                ""')
1155                 addToRC(r'\converter lilypond-book pdflatex "lilypond-book --safe --pdf --latex-program=pdflatex --lily-output-dir=ly-pdf $$i" ""')
1156                 addToRC(r'\converter lilypond-book-ja platex "lilypond-book --safe --pdf --latex-program=platex --lily-output-dir=ly-pdf $$i" ""')
1157                 addToRC(r'\converter lilypond-book xetex    "lilypond-book --safe --pdf --latex-program=xelatex --lily-output-dir=ly-pdf $$i"  ""')
1158                 addToRC(r'\converter lilypond-book luatex   "lilypond-book --safe --pdf --latex-program=lualatex --lily-output-dir=ly-pdf $$i" ""')
1159                 addToRC(r'\converter lilypond-book dviluatex "lilypond-book --safe --latex-program=dvilualatex --lily-output-dir=ly-eps $$i" ""')
1160                 logger.info('+  found LilyPond-book version %s.' % version_number)
1161             else:
1162                 logger.info('+  found LilyPond-book, but version %s is too old.' % version_number)
1163         else:
1164             logger.info('+  found LilyPond-book, but could not extract version number.')
1165     #
1166     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
1167         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""' ])
1168     #
1169     # Currently, lyxpak outputs a gzip compressed tar archive on *nix
1170     # and a zip archive on Windows.
1171     # So, we configure the appropriate version according to the platform.
1172     cmd = r'\converter lyx %s "python -tt $$s/scripts/lyxpak.py $$r/$$f" ""'
1173     if os.name == 'nt':
1174         addToRC(r'\Format lyxzip     zip    "LyX Archive (zip)"     "" "" ""  "document,menu=export"    ""')
1175         addToRC(cmd % "lyxzip")
1176     else:
1177         addToRC(r'\Format lyxgz      gz     "LyX Archive (tar.gz)"  "" "" ""  "document,menu=export"    ""')
1178         addToRC(cmd % "lyxgz")
1179
1180     #
1181     # FIXME: no rc_entry? comment it out
1182     # checkProg('Image converter', ['convert $$i $$o'])
1183     #
1184     # Entries that do not need checkProg
1185     addToRC(r'''
1186 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
1187 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
1188 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
1189 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.3 -o $$o $$i"     ""
1190 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o $$i"     ""
1191 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.5 -o $$o $$i"     ""
1192 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.6 -o $$o $$i"     ""
1193 \converter lyx        lyx20x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.0 -o $$o $$i"     ""
1194 \converter lyx        lyx21x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.1 -o $$o $$i"     ""
1195 \converter lyx        lyx22x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.2 -o $$o $$i"     ""
1196 \converter lyx        lyx23x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.3 -o $$o $$i"     ""
1197 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c big5   $$i"   ""
1198 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_jp $$i"   ""
1199 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_kr $$i"   ""
1200 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5   -o $$o $$i"  ""
1201 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -o $$o $$i"  ""
1202 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -o $$o $$i"  ""
1203 \converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py --png"    ""
1204 \converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py --ppm"    ""
1205 ''')
1206
1207
1208 def checkDocBook():
1209     ''' Check docbook '''
1210     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
1211         rc_entry = [
1212             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
1213 \converter docbook    html       "sgmltools -b html $$i"        ""
1214 \converter docbook    ps         "sgmltools -b ps $$i"  ""''',
1215             r'''\converter docbook    dvi        "db2dvi $$i"   ""
1216 \converter docbook    html       "db2html $$i"  ""''',
1217             r'''\converter docbook    dvi        ""     ""
1218 \converter docbook    html       "" ""''',
1219             r'''\converter docbook    dvi        ""     ""
1220 \converter docbook    html       ""     ""'''])
1221     #
1222     if DOCBOOK != '':
1223         return ('yes', 'true', '\\def\\hasdocbook{yes}')
1224     else:
1225         return ('no', 'false', '')
1226
1227
1228 def checkOtherEntries():
1229     ''' entries other than Format and Converter '''
1230     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
1231         rc_entry = [ r'\chktex_command "%%"' ])
1232     checkProgAlternatives('BibTeX or alternative programs',
1233         ['bibtex', 'bibtex8', 'biber'],
1234         rc_entry = [ r'\bibtex_command "automatic"' ],
1235         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
1236     checkProgAlternatives('a specific Japanese BibTeX variant',
1237         ['pbibtex', 'upbibtex', 'jbibtex', 'bibtex', 'biber'],
1238         rc_entry = [ r'\jbibtex_command "automatic"' ],
1239         alt_rc_entry = [ r'\jbibtex_alternatives "%%"' ])
1240     checkProgAlternatives('available index processors',
1241         ['texindy', 'makeindex -c -q', 'xindy'],
1242         rc_entry = [ r'\index_command "%%"' ],
1243         alt_rc_entry = [ r'\index_alternatives "%%"' ])
1244     checkProg('an index processor appropriate to Japanese',
1245         ['mendex -c -q', 'jmakeindex -c -q', 'makeindex -c -q'],
1246         rc_entry = [ r'\jindex_command "%%"' ])
1247     checkProg('the splitindex processor', ['splitindex.pl', 'splitindex',
1248         'splitindex.class'], rc_entry = [ r'\splitindex_command "%%"' ])
1249     checkProg('a nomenclature processor', ['makeindex'],
1250         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
1251     checkProg('a python-pygments driver command', ['pygmentize'],
1252         rc_entry = [ r'\pygmentize_command "%%"' ])
1253     ## FIXME: OCTAVE is not used anywhere
1254     # path, OCTAVE = checkProg('Octave', ['octave'])
1255     ## FIXME: MAPLE is not used anywhere
1256     # path, MAPLE = checkProg('Maple', ['maple'])
1257     # Add the rest of the entries (no checkProg is required)
1258     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
1259 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
1260 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
1261 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
1262 ''')
1263
1264
1265 def processLayoutFile(file, bool_docbook):
1266     ''' process layout file and get a line of result
1267
1268         Declare lines look like this:
1269
1270         \DeclareLaTeXClass[<requirements>]{<description>}
1271
1272         Optionally, a \DeclareCategory line follows:
1273
1274         \DeclareCategory{<category>}
1275
1276         So for example (article.layout, scrbook.layout, svjog.layout)
1277
1278         \DeclareLaTeXClass{article}
1279         \DeclareCategory{Articles}
1280
1281         \DeclareLaTeXClass[scrbook]{book (koma-script)}
1282         \DeclareCategory{Books}
1283
1284         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
1285
1286         we'd expect this output:
1287
1288         "article" "article" "article" "false" "article.cls" "Articles"
1289         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls" "Books"
1290         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo" ""
1291     '''
1292     def checkForClassExtension(x):
1293         '''if the extension for a latex class is not
1294            provided, add .cls to the classname'''
1295         if not b'.' in x:
1296             return x.strip() + b'.cls'
1297         else:
1298             return x.strip()
1299     classname = file.split(os.sep)[-1].split('.')[0]
1300     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
1301     p = re.compile(b'\s*#\s*\\\\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}\s*$')
1302     q = re.compile(b'\s*#\s*\\\\DeclareCategory{(.*)}\s*$')
1303     classdeclaration = b""
1304     categorydeclaration = b'""'
1305     for line in open(file, 'rb').readlines():
1306         res = p.match(line)
1307         qres = q.match(line)
1308         if res != None:
1309             (classtype, optAll, opt, opt1, desc) = res.groups()
1310             avai = {b'LaTeX':b'false', b'DocBook':bool_docbook.encode('ascii')}[classtype]
1311             if opt == None:
1312                 opt = classname.encode('ascii')
1313                 prereq_latex = checkForClassExtension(classname.encode('ascii'))
1314             else:
1315                 prereq_list = optAll[1:-1].split(b',')
1316                 prereq_list = list(map(checkForClassExtension, prereq_list))
1317                 prereq_latex = b','.join(prereq_list)
1318             prereq_docbook = {'true':b'', 'false':b'docbook'}[bool_docbook]
1319             prereq = {b'LaTeX':prereq_latex, b'DocBook':prereq_docbook}[classtype]
1320             classdeclaration = (b'"%s" "%s" "%s" "%s" "%s"'
1321                                % (classname, opt, desc, avai, prereq))
1322             if categorydeclaration != b'""':
1323                 return classdeclaration + b" " + categorydeclaration
1324         if qres != None:
1325              categorydeclaration = b'"%s"' % (qres.groups()[0])
1326              if classdeclaration != b"":
1327                  return classdeclaration + b" " + categorydeclaration
1328     if classdeclaration != b"":
1329         return classdeclaration + b" " + categorydeclaration
1330     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
1331     return b""
1332
1333
1334 def checkLatexConfig(check_config, bool_docbook):
1335     ''' Explore the LaTeX configuration
1336         Return None (will be passed to sys.exit()) for success.
1337     '''
1338     msg = 'checking LaTeX configuration... '
1339     # if --without-latex-config is forced, or if there is no previous
1340     # version of textclass.lst, re-generate a default file.
1341     if not os.path.isfile('textclass.lst') or not check_config:
1342         # remove the files only if we want to regenerate
1343         removeFiles(['textclass.lst', 'packages.lst'])
1344         #
1345         # Then, generate a default textclass.lst. In case configure.py
1346         # fails, we still have something to start lyx.
1347         logger.info(msg + ' default values')
1348         logger.info('+checking list of textclasses... ')
1349         tx = open('textclass.lst', 'wb')
1350         tx.write(b'''
1351 # This file declares layouts and their associated definition files
1352 # (include dir. relative to the place where this file is).
1353 # It contains only default values, since chkconfig.ltx could not be run
1354 # for some reason. Run ./configure.py if you need to update it after a
1355 # configuration change.
1356 ''')
1357         # build the list of available layout files and convert it to commands
1358         # for chkconfig.ltx
1359         foundClasses = []
1360         for file in (glob.glob(os.path.join('layouts', '*.layout'))
1361                      + glob.glob(os.path.join(srcdir, 'layouts', '*.layout'))):
1362             # valid file?
1363             if not os.path.isfile(file):
1364                 continue
1365             # get stuff between /xxxx.layout .
1366             classname = file.split(os.sep)[-1].split('.')[0]
1367             #  tr ' -' '__'`
1368             cleanclass = classname.replace(' ', '_')
1369             cleanclass = cleanclass.replace('-', '_')
1370             # make sure the same class is not considered twice
1371             if foundClasses.count(cleanclass) == 0: # not found before
1372                 foundClasses.append(cleanclass)
1373                 retval = processLayoutFile(file, bool_docbook)
1374                 if retval != b"":
1375                     tx.write(retval + os.linesep)
1376         tx.close()
1377         logger.info('\tdone')
1378     if not os.path.isfile('packages.lst') or not check_config:
1379         logger.info('+generating default list of packages... ')
1380         removeFiles(['packages.lst'])
1381         tx = open('packages.lst', 'w')
1382         tx.close()
1383         logger.info('\tdone')
1384     if not check_config:
1385         return None
1386     # the following will generate textclass.lst.tmp, and packages.lst.tmp
1387     logger.info(msg + '\tauto')
1388     removeFiles(['chkconfig.classes', 'chkconfig.vars', 'chklayouts.tex',
1389         'wrap_chkconfig.ltx'])
1390     rmcopy = False
1391     if not os.path.isfile( 'chkconfig.ltx' ):
1392         shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
1393         rmcopy = True
1394     writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
1395     # Construct the list of classes to test for.
1396     # build the list of available layout files and convert it to commands
1397     # for chkconfig.ltx
1398     declare = re.compile(b'\\s*#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*(\[([^,]*)(,.*)*\])*\\s*{(.*)}\\s*$')
1399     category = re.compile(b'\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
1400     empty = re.compile(b'\\s*$')
1401     testclasses = list()
1402     for file in (glob.glob( os.path.join('layouts', '*.layout') )
1403                  + glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) ):
1404         nodeclaration = False
1405         if not os.path.isfile(file):
1406             continue
1407         classname = file.split(os.sep)[-1].split('.')[0]
1408         decline = b""
1409         catline = b""
1410         for line in open(file, 'rb').readlines():
1411             if not empty.match(line) and line[0] != b'#'[0]:
1412                 if decline == b"":
1413                     logger.warning("Failed to find valid \Declare line "
1414                         "for layout file `%s'.\n\t=> Skipping this file!" % file)
1415                     nodeclaration = True
1416                 # A class, but no category declaration. Just break.
1417                 break
1418             if declare.match(line) != None:
1419                 decline = b"\\TestDocClass{%s}{%s}" \
1420                            % (classname.encode('ascii'), line[1:].strip())
1421                 testclasses.append(decline)
1422             elif category.match(line) != None:
1423                 catline = (b"\\DeclareCategory{%s}{%s}"
1424                            % (classname.encode('ascii'),
1425                               category.match(line).groups()[0]))
1426                 testclasses.append(catline)
1427             if catline == b"" or decline == b"":
1428                 continue
1429             break
1430         if nodeclaration:
1431             continue
1432     testclasses.sort()
1433     cl = open('chklayouts.tex', 'wb')
1434     for line in testclasses:
1435         cl.write(line + b'\n')
1436     cl.close()
1437     #
1438     # we have chklayouts.tex, then process it
1439     latex_out = cmdOutput(LATEX + ' wrap_chkconfig.ltx', True)
1440     while True:
1441         line = latex_out.readline()
1442         if not line:
1443             break;
1444         if line.startswith('+'):
1445             logger.info(line.strip())
1446     # if the command succeeds, None will be returned
1447     ret = latex_out.close()
1448     #
1449     # remove the copied file
1450     if rmcopy:
1451         removeFiles( [ 'chkconfig.ltx' ] )
1452     #
1453     # values in chkconfig were only used to set
1454     # \font_encoding, which is obsolete
1455 #    values = {}
1456 #    for line in open('chkconfig.vars').readlines():
1457 #        key, val = re.sub('-', '_', line).split('=')
1458 #        val = val.strip()
1459 #        values[key] = val.strip("'")
1460     # if configure successed, move textclass.lst.tmp to textclass.lst
1461     # and packages.lst.tmp to packages.lst
1462     if (os.path.isfile('textclass.lst.tmp')
1463           and len(open('textclass.lst.tmp').read()) > 0
1464         and os.path.isfile('packages.lst.tmp')
1465           and len(open('packages.lst.tmp').read()) > 0):
1466         shutil.move('textclass.lst.tmp', 'textclass.lst')
1467         shutil.move('packages.lst.tmp', 'packages.lst')
1468     return ret
1469
1470
1471 def checkModulesConfig():
1472   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
1473
1474   logger.info('+checking list of modules... ')
1475   tx = open('lyxmodules.lst', 'wb')
1476   tx.write(b'''## This file declares modules and their associated definition files.
1477 ## It has been automatically generated by configure
1478 ## Use "Options/Reconfigure" if you need to update it after a
1479 ## configuration change.
1480 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1481 ''')
1482
1483   # build the list of available modules
1484   seen = []
1485   # note that this searches the local directory first, then the
1486   # system directory. that way, we pick up the user's version first.
1487   for file in (glob.glob( os.path.join('layouts', '*.module') )
1488                + glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) ):
1489       # valid file?
1490       logger.info(file)
1491       if not os.path.isfile(file):
1492           continue
1493
1494       filename = file.split(os.sep)[-1]
1495       filename = filename[:-7]
1496       if seen.count(filename):
1497           continue
1498
1499       seen.append(filename)
1500       retval = processModuleFile(file, filename.encode('ascii'), bool_docbook)
1501       if retval != b"":
1502           tx.write(retval)
1503   tx.close()
1504   logger.info('\tdone')
1505
1506
1507 def processModuleFile(file, filename, bool_docbook):
1508     ''' process module file and get a line of result
1509
1510         The top of a module file should look like this:
1511           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1512           #DescriptionBegin
1513           #...body of description...
1514           #DescriptionEnd
1515           #Requires: [list of required modules]
1516           #Excludes: [list of excluded modules]
1517           #Category: [category name]
1518         The last three lines are optional (though do give a category).
1519         We expect output:
1520           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1521     '''
1522     remods = re.compile(b'\s*#\s*\\\\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1523     rereqs = re.compile(b'\s*#+\s*Requires: (.*)')
1524     reexcs = re.compile(b'\s*#+\s*Excludes: (.*)')
1525     recaty = re.compile(b'\s*#+\s*Category: (.*)')
1526     redbeg = re.compile(b'\s*#+\s*DescriptionBegin\s*$')
1527     redend = re.compile(b'\s*#+\s*DescriptionEnd\s*$')
1528
1529     modname = desc = pkgs = req = excl = catgy = b""
1530     readingDescription = False
1531     descLines = []
1532
1533     for line in open(file, 'rb').readlines():
1534       if readingDescription:
1535         res = redend.match(line)
1536         if res != None:
1537           readingDescription = False
1538           desc = b" ".join(descLines)
1539           # Escape quotes.
1540           desc = desc.replace(b'"', b'\\"')
1541           continue
1542         descLines.append(line[1:].strip())
1543         continue
1544       res = redbeg.match(line)
1545       if res != None:
1546         readingDescription = True
1547         continue
1548       res = remods.match(line)
1549       if res != None:
1550           (pkgs, modname) = res.groups()
1551           if pkgs == None:
1552             pkgs = b""
1553           else:
1554             tmp = [s.strip() for s in pkgs.split(b",")]
1555             pkgs = b",".join(tmp)
1556           continue
1557       res = rereqs.match(line)
1558       if res != None:
1559         req = res.group(1)
1560         tmp = [s.strip() for s in req.split(b"|")]
1561         req = b"|".join(tmp)
1562         continue
1563       res = reexcs.match(line)
1564       if res != None:
1565         excl = res.group(1)
1566         tmp = [s.strip() for s in excl.split(b"|")]
1567         excl = b"|".join(tmp)
1568         continue
1569       res = recaty.match(line)
1570       if res != None:
1571         catgy = res.group(1)
1572         continue
1573
1574     if modname == b"":
1575       logger.warning("Module file without \DeclareLyXModule line. ")
1576       return b""
1577
1578     if pkgs != b"":
1579         # this module has some latex dependencies:
1580         # append the dependencies to chkmodules.tex,
1581         # which is \input'ed by chkconfig.ltx
1582         testpackages = list()
1583         for pkg in pkgs.split(b","):
1584             if b"->" in pkg:
1585                 # this is a converter dependency: skip
1586                 continue
1587             if pkg.endswith(b".sty"):
1588                 pkg = pkg[:-4]
1589             testpackages.append("\\TestPackage{%s}" % (pkg.decode('ascii'),))
1590         cm = open('chkmodules.tex', 'a')
1591         for line in testpackages:
1592             cm.write(line + '\n')
1593         cm.close()
1594
1595     return (b'"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n'
1596             % (modname, filename, desc, pkgs, req, excl, catgy))
1597
1598
1599 def checkCiteEnginesConfig():
1600   removeFiles(['lyxciteengines.lst', 'chkciteengines.tex'])
1601
1602   logger.info('+checking list of cite engines... ')
1603   tx = open('lyxciteengines.lst', 'wb')
1604   tx.write(b'''## This file declares cite engines and their associated definition files.
1605 ## It has been automatically generated by configure
1606 ## Use "Options/Reconfigure" if you need to update it after a
1607 ## configuration change.
1608 ## "CiteEngineName" "filename" "CiteEngineType" "CiteFramework" "DefaultBiblio" "Description" "Packages"
1609 ''')
1610
1611   # build the list of available modules
1612   seen = []
1613   # note that this searches the local directory first, then the
1614   # system directory. that way, we pick up the user's version first.
1615   for file in glob.glob( os.path.join('citeengines', '*.citeengine') ) + \
1616       glob.glob( os.path.join(srcdir, 'citeengines', '*.citeengine' ) ) :
1617       # valid file?
1618       logger.info(file)
1619       if not os.path.isfile(file):
1620           continue
1621
1622       filename = file.split(os.sep)[-1]
1623       filename = filename[:-11]
1624       if seen.count(filename):
1625           continue
1626
1627       seen.append(filename)
1628       retval = processCiteEngineFile(file, filename.encode('ascii'), bool_docbook)
1629       if retval != b"":
1630           tx.write(retval)
1631   tx.close()
1632   logger.info('\tdone')
1633
1634
1635 def processCiteEngineFile(file, filename, bool_docbook):
1636     ''' process cite engines file and get a line of result
1637
1638         The top of a cite engine file should look like this:
1639           #\DeclareLyXCiteEngine[LaTeX Packages]{CiteEngineName}
1640           #DescriptionBegin
1641           #...body of description...
1642           #DescriptionEnd
1643         We expect output:
1644           "CiteEngineName" "filename" "CiteEngineType" "CiteFramework" "DefaultBiblio" "Description" "Packages"
1645     '''
1646     remods = re.compile(b'\s*#\s*\\\\DeclareLyXCiteEngine\s*(?:\[([^]]*?)\])?{(.*)}')
1647     redbeg = re.compile(b'\s*#+\s*DescriptionBegin\s*$')
1648     redend = re.compile(b'\s*#+\s*DescriptionEnd\s*$')
1649     recet = re.compile(b'\s*CiteEngineType\s*(.*)')
1650     redb = re.compile(b'\s*DefaultBiblio\s*(.*)')
1651     resfm = re.compile(b'\s*CiteFramework\s*(.*)')
1652
1653     modname = desc = pkgs = cet = db = cfm = ""
1654     readingDescription = False
1655     descLines = []
1656
1657     for line in open(file, 'rb').readlines():
1658       if readingDescription:
1659         res = redend.match(line)
1660         if res != None:
1661           readingDescription = False
1662           desc = b" ".join(descLines)
1663           # Escape quotes.
1664           desc = desc.replace(b'"', b'\\"')
1665           continue
1666         descLines.append(line[1:].strip())
1667         continue
1668       res = redbeg.match(line)
1669       if res != None:
1670         readingDescription = True
1671         continue
1672       res = remods.match(line)
1673       if res != None:
1674           (pkgs, modname) = res.groups()
1675           if pkgs == None:
1676             pkgs = b""
1677           else:
1678             tmp = [s.strip() for s in pkgs.split(b",")]
1679             pkgs = b",".join(tmp)
1680           continue
1681       res = recet.match(line)
1682       if res != None:
1683         cet = res.group(1)
1684         continue
1685       res = redb.match(line)
1686       if res != None:
1687         db = res.group(1)
1688         continue
1689       res = resfm.match(line)
1690       if res != None:
1691         cfm = res.group(1)
1692         continue
1693
1694     if modname == b"":
1695       logger.warning("Cite Engine File file without \DeclareLyXCiteEngine line. ")
1696       return b""
1697
1698     if pkgs != b"":
1699         # this cite engine has some latex dependencies:
1700         # append the dependencies to chkciteengines.tex,
1701         # which is \input'ed by chkconfig.ltx
1702         testpackages = list()
1703         for pkg in pkgs.split(b","):
1704             if b"->" in pkg:
1705                 # this is a converter dependency: skip
1706                 continue
1707             if pkg.endswith(b".sty"):
1708                 pkg = pkg[:-4]
1709             testpackages.append("\\TestPackage{%s}" % (pkg.decode('ascii'),))
1710         cm = open('chkciteengines.tex', 'a')
1711         for line in testpackages:
1712             cm.write(line + '\n')
1713         cm.close()
1714
1715     return (b'"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, cet, cfm, db, desc, pkgs))
1716
1717
1718 def checkXTemplates():
1719   removeFiles(['xtemplates.lst'])
1720
1721   logger.info('+checking list of external templates... ')
1722   tx = open('xtemplates.lst', 'w')
1723   tx.write('''## This file lists external templates.
1724 ## It has been automatically generated by configure
1725 ## Use "Options/Reconfigure" if you need to update it after a
1726 ## configuration change.
1727 ''')
1728
1729   # build the list of available templates
1730   seen = []
1731   # note that this searches the local directory first, then the
1732   # system directory. that way, we pick up the user's version first.
1733   for file in glob.glob( os.path.join('xtemplates', '*.xtemplate') ) + \
1734       glob.glob( os.path.join(srcdir, 'xtemplates', '*.xtemplate' ) ) :
1735       # valid file?
1736       logger.info(file)
1737       if not os.path.isfile(file):
1738           continue
1739
1740       filename = file.split(os.sep)[-1]
1741       if seen.count(filename):
1742           continue
1743
1744       seen.append(filename)
1745       if filename != "":
1746           tx.write(filename + "\n")
1747   tx.close()
1748   logger.info('\tdone')
1749
1750
1751 def checkTeXAllowSpaces():
1752     ''' Let's check whether spaces are allowed in TeX file names '''
1753     tex_allows_spaces = 'false'
1754     if lyx_check_config:
1755         msg = "Checking whether TeX allows spaces in file names... "
1756         writeToFile('a b.tex', r'\message{working^^J}' )
1757         if LATEX != '':
1758             if os.name == 'nt' or sys.platform == 'cygwin':
1759                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}\makeatletter\@@end" """)
1760             else:
1761                 latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}\makeatletter\@@end' """)
1762         else:
1763             latex_out = ''
1764         if 'working' in latex_out:
1765             logger.info(msg + 'yes')
1766             tex_allows_spaces = 'true'
1767         else:
1768             logger.info(msg + 'no')
1769             tex_allows_spaces = 'false'
1770         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1771         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1772
1773
1774 def rescanTeXFiles():
1775     ''' Run kpsewhich to update information about TeX files '''
1776     logger.info("+Indexing TeX files... ")
1777     tfscript = os.path.join(srcdir, 'scripts', 'TeXFiles.py')
1778     if not os.path.isfile(tfscript):
1779         logger.error("configure: error: cannot find TeXFiles.py script")
1780         sys.exit(1)
1781     interpreter = sys.executable
1782     if interpreter == '':
1783         interpreter = "python"
1784     tfp = cmdOutput('"%s" -tt "%s"' % (interpreter, tfscript))
1785     logger.info(tfp)
1786     logger.info("\tdone")
1787
1788
1789 def removeTempFiles():
1790     # Final clean-up
1791     if not lyx_keep_temps:
1792         removeFiles(['chkconfig.vars', 'chklatex.ltx', 'chklatex.log',
1793             'chklayouts.tex', 'chkmodules.tex', 'chkciteengines.tex',
1794             'missfont.log', 'wrap_chkconfig.ltx', 'wrap_chkconfig.log'])
1795
1796
1797 if __name__ == '__main__':
1798     lyx_check_config = True
1799     lyx_kpsewhich = True
1800     outfile = 'lyxrc.defaults'
1801     lyxrc_fileformat = 27
1802     rc_entries = ''
1803     lyx_keep_temps = False
1804     version_suffix = ''
1805     lyx_binary_dir = ''
1806     ## Parse the command line
1807     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1808         if op in [ '-help', '--help', '-h' ]:
1809             print('''Usage: configure [options]
1810 Options:
1811     --help                   show this help lines
1812     --keep-temps             keep temporary files (for debug. purposes)
1813     --without-kpsewhich      do not update TeX files information via kpsewhich
1814     --without-latex-config   do not run LaTeX to determine configuration
1815     --with-version-suffix=suffix suffix of binary installed files
1816     --binary-dir=directory   directory of binary installed files
1817 ''')
1818             sys.exit(0)
1819         elif op == '--without-kpsewhich':
1820             lyx_kpsewhich = False
1821         elif op == '--without-latex-config':
1822             lyx_check_config = False
1823         elif op == '--keep-temps':
1824             lyx_keep_temps = True
1825         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1826             version_suffix = op[22:]
1827         elif op[0:13] == '--binary-dir=':
1828             lyx_binary_dir = op[13:]
1829         else:
1830             print("Unknown option %s" % op)
1831             sys.exit(1)
1832     #
1833     # check if we run from the right directory
1834     srcdir = os.path.dirname(sys.argv[0])
1835     if srcdir == '':
1836         srcdir = '.'
1837     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1838         logger.error("configure: error: cannot find chkconfig.ltx script")
1839         sys.exit(1)
1840     setEnviron()
1841     if sys.platform == 'darwin' and len(version_suffix) > 0:
1842         checkUpgrade()
1843     createDirectories()
1844     dtl_tools = checkDTLtools()
1845     ## Write the first part of outfile
1846     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1847 # script. It contains default settings that have been determined by
1848 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1849 # want to customize LyX, use LyX' Preferences dialog or modify directly
1850 # the "preferences" file instead. Any setting in that file will
1851 # override the values given here.
1852
1853 Format %i
1854
1855 ''' % lyxrc_fileformat)
1856     # check latex
1857     LATEX = checkLatex(dtl_tools)
1858     # check java and perl before any checkProg that may require them
1859     java = checkProg('a java interpreter', ['java'])[1]
1860     perl = checkProg('a perl interpreter', ['perl'])[1]
1861     (inkscape_path, inkscape_gui) = os.path.split(checkInkscape())
1862     # On Windows, we need to call the "inkscape.com" wrapper
1863     # for command line purposes. Other OSes do not differentiate.
1864     inkscape_cl = inkscape_gui
1865     if os.name == 'nt':
1866         inkscape_cl = inkscape_gui.replace('.exe', '.com')
1867     # On MacOSX, Inkscape requires full path file arguments. This
1868     # is not needed on Linux and Win and even breaks the latter.
1869     inkscape_fileprefix = ""
1870     if sys.platform == 'darwin':
1871         inkscape_fileprefix = "$$p"
1872     checkFormatEntries(dtl_tools)
1873     checkConverterEntries()
1874     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1875     checkTeXAllowSpaces()
1876     windows_style_tex_paths = checkTeXPaths()
1877     if windows_style_tex_paths != '':
1878         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1879     checkOtherEntries()
1880     if lyx_kpsewhich:
1881         rescanTeXFiles()
1882     checkModulesConfig()
1883     checkCiteEnginesConfig()
1884     checkXTemplates()
1885     # --without-latex-config can disable lyx_check_config
1886     ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
1887     removeTempFiles()
1888     # The return error code can be 256. Because most systems expect an error code
1889     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1890     # a None for success, 'ret is not None' is used to exit.
1891     sys.exit(ret is not None)