]> git.lyx.org Git - lyx.git/blob - development/scons/scons_utils.py
Scons: create libs directory for res file
[lyx.git] / development / scons / scons_utils.py
1 # vi:filetype=python:expandtab:tabstop=4:shiftwidth=4
2 #
3 # file scons_utils.py
4 #
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 # This file defines all the utility functions for the
12 # scons-based build system of lyx
13 #
14
15 import os, sys, re, shutil, glob
16 from SCons.Util import WhereIs
17
18
19 def getVerFromConfigure(path):
20     " get lyx version from the AC_INIT line of configure.ac "
21     try:
22         config = open(os.path.join(path, 'configure.ac'))
23     except:
24         print "Can not open configure.ac. "
25         return 'x.x.x'
26     # find a line like follows
27     # AC_INIT(LyX,1.4.4svn,[lyx-devel@lists.lyx.org],[lyx])
28     pat = re.compile('AC_INIT\([^,]+,([^,]+),')
29     for line in config.readlines():
30         if pat.match(line):
31             (version,) = pat.match(line).groups()
32             return version.strip()
33     return 'x.x.x'
34
35
36
37 def writeToFile(filename, lines, append = False):
38     " utility function: write or append lines to filename "
39     # create directory if needed
40     dir = os.path.split(filename)[0]
41     if dir != '' and not os.path.isdir(dir):
42         os.makedirs(dir)
43     if append:
44         file = open(filename, 'a')
45     else:
46         file = open(filename, 'w')
47     file.write(lines)
48     file.close()
49
50
51 def env_subst(target, source, env):
52     ''' subst variables in source by those in env, and output to target
53         source and target are scons File() objects
54
55         %key% (not key itself) is an indication of substitution
56     '''
57     assert len(target) == 1
58     assert len(source) == 1
59     target_file = file(str(target[0]), "w")
60     source_file = file(str(source[0]), "r")
61
62     contents = source_file.read()
63     for k, v in env.items():
64         try:
65             val = env.subst('$'+k)
66             # temporary fix for the \Resource backslash problem
67             val = val.replace('\\', '/')
68             # multi-line replacement
69             val = val.replace('\n',r'\\n\\\n')
70             contents = re.sub('@'+k+'@', val, contents)
71         except:
72             pass
73     target_file.write(contents + "\n")
74     target_file.close()
75     #st = os.stat(str(source[0]))
76     #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
77
78
79 def createResFromIcon(env, icon_file, rc_file):
80     ''' create a rc file with icon, and return res file (windows only) '''
81     if os.name == 'nt':
82         rc_name = env.File(rc_file).abspath
83         dir = os.path.split(rc_name)[0]
84         if not os.path.isdir(dir):
85             os.makedirs(dir)
86         rc = open(rc_name, 'w')
87         print >> rc, 'IDI_ICON1  ICON DISCARDABLE "%s"' % \
88             os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'development', 'win32',
89                 'packaging', 'icons', icon_file)
90         rc.close()
91         return env.RES(rc_name)
92     else:
93         return []
94
95
96 #
97 # autoconf tests
98 #
99
100 def checkPkgConfig(conf, version):
101     ''' Return false if pkg_config does not exist, or is too old '''
102     conf.Message('Checking for pkg-config...')
103     ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
104     conf.Result(ret)
105     return ret
106
107
108 def checkPackage(conf, pkg):
109     ''' check if pkg is under the control of conf '''
110     conf.Message('Checking for package %s...' % pkg)
111     ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
112     conf.Result(ret)
113     return ret
114
115
116 def checkMkdirOneArg(conf):
117     check_mkdir_one_arg_source = """
118 #include <sys/stat.h>
119 int main()
120 {
121     mkdir("somedir");
122 }
123 """
124     conf.Message('Checking for the number of args for mkdir... ')
125     ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
126         conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
127         conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
128     if ret:
129         conf.Result('one')
130     else:
131         conf.Result('two')
132     return ret
133
134
135 def checkCXXGlobalCstd(conf):
136     ''' Check the use of std::tolower or tolower '''
137     check_global_cstd_source = '''
138 #include <cctype>
139 using std::tolower;
140 int main()
141 {
142     return 0;
143 }
144 '''
145     conf.Message('Check for the use of global cstd... ')
146     ret = conf.TryLink(check_global_cstd_source, '.c')
147     conf.Result(ret)
148     return ret
149
150
151 def checkSelectArgType(conf):
152     ''' Adapted from autoconf '''
153     conf.Message('Checking for arg types for select... ')
154     for arg234 in ['fd_set *', 'int *', 'void *']:
155         for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
156             for arg5 in ['struct timeval *', 'const struct timeval *']:
157                 check_select_source = '''
158 #if HAVE_SYS_SELECT_H
159 # include <sys/select.h>
160 #endif
161 #if HAVE_SYS_SOCKET_H
162 # include <sys/socket.h>
163 #endif
164 extern int select (%s, %s, %s, %s, %s);
165 int main()
166 {
167     return(0);
168 }
169 ''' % (arg1, arg234, arg234, arg234, arg5)
170                 ret = conf.TryLink(check_select_source, '.c')
171                 if ret:
172                     conf.Result(ret)
173                     return (arg1, arg234, arg5)
174     conf.Result('no (use default)')
175     return ('int', 'int *', 'struct timeval *')
176
177
178 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
179     ''' look for boost libraries
180       libs: library names
181       lib_paths: try these paths for boost libraries
182       inc_paths: try these paths for boost headers
183       versions:   supported boost versions
184       isDebug:   if true, use debug libraries
185     '''
186     conf.Message('Checking for boost library %s... ' % ', '.join(libs))
187     libprefix = conf.env['LIBPREFIX']
188     libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
189     found_lib = False
190     found_inc = False
191     lib_names = []
192     lib_path = None
193     inc_path = None
194     for path in lib_paths:
195         conf.Log("Looking into %s\n" % path)
196         for lib in libs:
197             # get all the libs, then filter for the right library
198             files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
199             # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
200             if len(files) > 0:
201                 conf.Log("Find boost libraries: %s\n" % files)
202                 # runtime code includes s,g,y,d,p,n, where we should look for
203                 # d,g,y for debug, s,p,n for release
204                 lib_files = []
205                 if isDebug:
206                     for ver in versions:
207                         lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
208                 else:
209                     for ver in versions:
210                         lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
211                 if len(lib_files) == 0:
212                     # use alternative libraries
213                     for ver in versions:
214                         lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
215                 if len(lib_files) > 0:
216                     # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
217                     name = lib_files[0].split(os.sep)[-1][len(libprefix):]
218                     lib_names.append(name.split('.')[0])
219                     conf.Log("Qualified libraries: %s\n" % lib_names)
220                 else:
221                     conf.Log("No qualified library is found.\n")
222                     break
223         if len(lib_names) == len(libs):
224             found_lib = True
225             lib_path = path
226             break
227     if not found_lib:
228         if len(lib_names) == 0:
229             conf.Log("No boost library is found\n")
230         else:
231             conf.Log("Found boost libraries: %s\n" % lib_names)
232         conf.Result('no')
233         return (None, None, None)
234     # check version number in boost/version.hpp
235     def isValidBoostDir(dir):
236         version_file = os.path.join(dir, 'boost', 'version.hpp')
237         if not os.path.isfile(version_file):
238             return False
239         version_file_content = open(version_file).read()
240         version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
241         return True in [x in version_file_content for x in version_strings]
242     # check for boost header file
243     for path in inc_paths:
244         conf.Log("Checking for inc path: %s\n" % path)
245         if isValidBoostDir(path):
246             inc_path = path
247             found_inc = True
248         else:   # check path/boost_1_xx_x/boost
249             dirs = glob.glob(os.path.join(path, 'boost-*'))
250             if len(dirs) > 0 and isValidBoostDir(dirs[0]):
251                 conf.Log("Checing for sub directory: %s\n" % dirs[0])
252                 inc_path = dirs[0]
253                 found_inc = True
254     # return result
255     if found_inc:
256         conf.Result('yes')
257         conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
258         return (lib_names, lib_path, inc_path)
259     else:
260         conf.Result('no')
261         return (None, None, None)
262
263
264 def checkCommand(conf, cmd):
265     ''' check the existence of a command
266         return full path to the command, or none
267     '''
268     conf.Message('Checking for command %s...' % cmd)
269     res = WhereIs(cmd)
270     conf.Result(res is not None)
271     return res
272
273
274 def checkLC_MESSAGES(conf):
275     ''' check the definition of LC_MESSAGES '''
276     check_LC_MESSAGES = '''
277 #include <locale.h>
278 int main()
279 {
280     return LC_MESSAGES;
281 }
282 '''
283     conf.Message('Check for LC_MESSAGES in locale.h... ')
284     ret = conf.TryLink(check_LC_MESSAGES, '.c')
285     conf.Result(ret)
286     return ret
287
288
289 def checkIconvConst(conf):
290     ''' check the declaration of iconv '''
291     check_iconv_const = '''
292 #include <iconv.h>
293 // this declaration will fail when there already exists a non const char** 
294 // version which returns size_t
295 double iconv(iconv_t cd,  char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
296 int main() {
297     return 0; 
298 }
299 '''
300     conf.Message('Check if the declaration of iconv needs const... ')
301     ret = conf.TryLink(check_iconv_const, '.c')
302     conf.Result(ret)
303     return ret
304
305
306 def checkSizeOfWChar(conf):
307     ''' check the size of wchar '''
308     check_sizeof_wchar = '''
309 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
310 int main()
311 {
312     return 0;
313 }
314 '''
315     conf.Message('Check the size of wchar_t... ')
316     if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
317         ret = 2
318     elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
319         ret = 4
320     else:
321         ret = 0
322     conf.Result(str(ret))
323     return ret
324
325
326 def createConfigFile(conf, config_file,
327     config_pre = '', config_post = '',
328     headers = [], functions = [], types = [], libs = [],
329     custom_tests = [], extra_items = []):
330     ''' create a configuration file, with options
331         config_file: which file to create
332         config_pre: first part of the config file
333         config_post: last part of the config file
334         headers: header files to check, in the form of a list of
335             ('file', 'HAVE_FILE', 'c'/'c++')
336         functions: functions to check, in the form of a list of
337             ('func', 'HAVE_func', 'include lines'/None)
338         types: types to check, in the form of a list of
339             ('type', 'HAVE_TYPE', 'includelines'/None)
340         libs: libraries to check, in the form of a list of
341             ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
342             or any of the libs exists if 'lib' is a list of libs.
343             Optionally, user can provide another key LIB_NAME, that will
344             be set to the detected lib (or None otherwise).
345         custom_tests: extra tests to perform, in the form of a list of
346             (test (True/False), 'key', 'desc', 'true config line', 'false config line')
347             If the last two are ignored, '#define key 1' '/*#undef key */'
348             will be used.
349         extra_items: extra configuration lines, in the form of a list of
350             ('config', 'description')
351     Return:
352         The result of each test, as a dictioanry of
353             res['XXX'] = True/False
354         XXX are keys defined in each argument.
355     '''
356     cont = config_pre + '\n'
357     result = {}
358     # add to this string, in appropriate format
359     def configString(lines, desc=''):
360         text = ''
361         if lines.strip() != '':
362             if desc != '':
363                 text += '/* ' + desc + ' */\n'
364             text += lines + '\n\n'
365         return text
366     #
367     # headers
368     for header in headers:
369         description = "Define to 1 if you have the <%s> header file." % header[0]
370         if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
371             (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
372             result[header[1]] = 1
373             cont += configString('#define %s 1' % header[1], desc = description)
374         else:
375             result[header[1]] = 0
376             cont += configString('/* #undef %s */' % header[1], desc = description)
377     # functions
378     for func in functions:
379         description = "Define to 1 if you have the `%s' function." % func[0]
380         if conf.CheckFunc(func[0], header=func[2]):
381             result[func[1]] = 1
382             cont += configString('#define %s 1' % func[1], desc = description)
383         else:
384             result[func[1]] = 0
385             cont += configString('/* #undef %s */' % func[1], desc = description)
386     # types
387     for t in types:
388         description = "Define to 1 if you have the `%s' type." % t[0]
389         if conf.CheckType(t[0], includes=t[2]):
390             result[t[1]] = 1
391             cont += configString('#define %s 1' % t[1], desc = description)
392         else:
393             result[t[1]] = 0
394             cont += configString('/* #undef %s */' % t[1],  desc = description)
395     # libraries
396     for lib in libs:
397         description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
398         if type(lib[0]) is type(''):
399             lib_list = [lib[0]]
400         else:
401             lib_list = lib[0]
402         # check if any of the lib exists
403         result[lib[1]] = 0
404         # if user want the name of the lib detected
405         if len(lib) == 3:
406             result[lib[2]] = None
407         for ll in lib_list:
408             if conf.CheckLib(ll):
409                 result[lib[1]] = 1
410                 if len(lib) == 3:
411                     result[lib[2]] = ll
412                 cont += configString('#define %s 1' % lib[1], desc = description)
413                 break
414         # if not found
415         if not result[lib[1]]:
416             cont += configString('/* #undef %s */' % lib[1], desc = description)
417     # custom tests
418     for test in custom_tests:
419         if test[0]:
420             result[test[1]] = 1
421             if len(test) == 3:
422                 cont += configString('#define %s 1' % test[1], desc = test[2])
423             else:
424                 cont += configString(test[3], desc = test[2])
425         else:
426             result[test[1]] = 0
427             if len(test) == 3:
428                 cont += configString('/* #undef %s */' % test[1], desc = test[2])
429             else:
430                 cont += configString(test[4], desc = test[2])
431     # extra items (no key is returned)
432     for item in extra_items:
433         cont += configString(item[0], desc = item[1])
434     # add the last part
435     cont += '\n' + config_post + '\n'
436     # write to file
437     writeToFile(config_file, cont)
438     return result
439
440
441 def installCygwinLDScript(path):
442     ''' Install i386pe.x-no-rdata '''
443     ld_script = os.path.join(path, 'i386pe.x-no-rdata')
444     script = open(ld_script, 'w')
445     script.write('''/* specific linker script avoiding .rdata sections, for normal executables
446 for a reference see
447 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
448 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
449 */
450 OUTPUT_FORMAT(pei-i386)
451 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
452 ENTRY(_mainCRTStartup)
453 SECTIONS
454 {
455   .text  __image_base__ + __section_alignment__  :
456   {
457     *(.init)
458     *(.text)
459     *(SORT(.text$*))
460     *(.glue_7t)
461     *(.glue_7)
462     ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
463                         LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*));  LONG (0);
464     ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
465                         LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*));  LONG (0);
466     *(.fini)
467     /* ??? Why is .gcc_exc here?  */
468     *(.gcc_exc)
469     PROVIDE (etext = .);
470     *(.gcc_except_table)
471   }
472   /* The Cygwin32 library uses a section to avoid copying certain data
473     on fork.  This used to be named ".data".  The linker used
474     to include this between __data_start__ and __data_end__, but that
475     breaks building the cygwin32 dll.  Instead, we name the section
476     ".data_cygwin_nocopy" and explictly include it after __data_end__. */
477   .data BLOCK(__section_alignment__) :
478   {
479     __data_start__ = . ;
480     *(.data)
481     *(.data2)
482     *(SORT(.data$*))
483     *(.rdata)
484     *(SORT(.rdata$*))
485     *(.eh_frame)
486     ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
487     __RUNTIME_PSEUDO_RELOC_LIST__ = .;
488     *(.rdata_runtime_pseudo_reloc)
489     ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
490     __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
491     __data_end__ = . ;
492     *(.data_cygwin_nocopy)
493   }
494   .rdata BLOCK(__section_alignment__) :
495   {
496   }
497   .pdata BLOCK(__section_alignment__) :
498   {
499     *(.pdata)
500   }
501   .bss BLOCK(__section_alignment__) :
502   {
503     __bss_start__ = . ;
504     *(.bss)
505     *(COMMON)
506     __bss_end__ = . ;
507   }
508   .edata BLOCK(__section_alignment__) :
509   {
510     *(.edata)
511   }
512   /DISCARD/ :
513   {
514     *(.debug$S)
515     *(.debug$T)
516     *(.debug$F)
517     *(.drectve)
518   }
519   .idata BLOCK(__section_alignment__) :
520   {
521     /* This cannot currently be handled with grouped sections.
522         See pe.em:sort_sections.  */
523     SORT(*)(.idata$2)
524     SORT(*)(.idata$3)
525     /* These zeroes mark the end of the import list.  */
526     LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
527     SORT(*)(.idata$4)
528     SORT(*)(.idata$5)
529     SORT(*)(.idata$6)
530     SORT(*)(.idata$7)
531   }
532   .CRT BLOCK(__section_alignment__) :
533   {
534     ___crt_xc_start__ = . ;
535     *(SORT(.CRT$XC*))  /* C initialization */
536     ___crt_xc_end__ = . ;
537     ___crt_xi_start__ = . ;
538     *(SORT(.CRT$XI*))  /* C++ initialization */
539     ___crt_xi_end__ = . ;
540     ___crt_xl_start__ = . ;
541     *(SORT(.CRT$XL*))  /* TLS callbacks */
542     /* ___crt_xl_end__ is defined in the TLS Directory support code */
543     ___crt_xp_start__ = . ;
544     *(SORT(.CRT$XP*))  /* Pre-termination */
545     ___crt_xp_end__ = . ;
546     ___crt_xt_start__ = . ;
547     *(SORT(.CRT$XT*))  /* Termination */
548     ___crt_xt_end__ = . ;
549   }
550   .tls BLOCK(__section_alignment__) :
551   {
552     ___tls_start__ = . ;
553     *(.tls)
554     *(.tls$)
555     *(SORT(.tls$*))
556     ___tls_end__ = . ;
557   }
558   .endjunk BLOCK(__section_alignment__) :
559   {
560     /* end is deprecated, don't use it */
561     PROVIDE (end = .);
562     PROVIDE ( _end = .);
563     __end__ = .;
564   }
565   .rsrc BLOCK(__section_alignment__) :
566   {
567     *(.rsrc)
568     *(SORT(.rsrc$*))
569   }
570   .reloc BLOCK(__section_alignment__) :
571   {
572     *(.reloc)
573   }
574   .stab BLOCK(__section_alignment__) (NOLOAD) :
575   {
576     *(.stab)
577   }
578   .stabstr BLOCK(__section_alignment__) (NOLOAD) :
579   {
580     *(.stabstr)
581   }
582   /* DWARF debug sections.
583     Symbols in the DWARF debugging sections are relative to the beginning
584     of the section.  Unlike other targets that fake this by putting the
585     section VMA at 0, the PE format will not allow it.  */
586   /* DWARF 1.1 and DWARF 2.  */
587   .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
588   {
589     *(.debug_aranges)
590   }
591   .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
592   {
593     *(.debug_pubnames)
594   }
595   /* DWARF 2.  */
596   .debug_info BLOCK(__section_alignment__) (NOLOAD) :
597   {
598     *(.debug_info) *(.gnu.linkonce.wi.*)
599   }
600   .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
601   {
602     *(.debug_abbrev)
603   }
604   .debug_line BLOCK(__section_alignment__) (NOLOAD) :
605   {
606     *(.debug_line)
607   }
608   .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
609   {
610     *(.debug_frame)
611   }
612   .debug_str BLOCK(__section_alignment__) (NOLOAD) :
613   {
614     *(.debug_str)
615   }
616   .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
617   {
618     *(.debug_loc)
619   }
620   .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
621   {
622     *(.debug_macinfo)
623   }
624   /* SGI/MIPS DWARF 2 extensions.  */
625   .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
626   {
627     *(.debug_weaknames)
628   }
629   .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
630   {
631     *(.debug_funcnames)
632   }
633   .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
634   {
635     *(.debug_typenames)
636   }
637   .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
638   {
639     *(.debug_varnames)
640   }
641   /* DWARF 3.  */
642   .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
643   {
644     *(.debug_ranges)
645   }
646 }
647 ''')
648     script.close()
649     return(ld_script)
650
651
652 def installCygwinPostinstallScript(path):
653     ''' Install lyx.sh '''
654     postinstall_script = os.path.join(path, 'lyx.sh')
655     script = open(postinstall_script, 'w')
656     script.write('''#!/bin/sh
657
658 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
659 # if it is not already there.
660 if [ -f /etc/fonts/local.conf ]; then
661     grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
662     if [ $? -ne 0 ]; then
663         sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
664         mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
665         fc-cache /usr/share/lyx/fonts
666     fi
667 fi
668     ''')
669     script.close()
670     return(postinstall_script)
671
672
673 try:
674     # these will be used under win32
675     import win32file
676     import win32event
677     import win32process
678     import win32security
679 except:
680     # does not matter if it fails on other systems
681     pass
682
683
684 class loggedSpawn:
685     def __init__(self, env, logfile, longarg, info):
686         # save the spawn system
687         self.env = env
688         self.logfile = logfile
689         # clear the logfile (it may not exist)
690         if logfile != '':
691             # this will overwrite existing content.
692             writeToFile(logfile, info, append=False)
693         #
694         self.longarg = longarg
695         # get hold of the old spawn? (necessary?)
696         self._spawn = env['SPAWN']
697
698     # define new SPAWN
699     def spawn(self, sh, escape, cmd, args, spawnenv):
700         # get command line
701         newargs = ' '.join(map(escape, args[1:]))
702         cmdline = cmd + " " + newargs
703         #
704         # if log is not empty, write to it
705         if self.logfile != '':
706             # this tend to be slow (?) but ensure correct output
707             # Note that cmdline may be long so I do not escape it
708             try:
709                 # since this is not an essential operation, proceed if things go wrong here.
710                 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
711             except:
712                 print "Warning: can not write to log file ", self.logfile
713         #
714         # if the command is not too long, use the old
715         if not self.longarg or len(cmdline) < 8000:
716             exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
717         else:
718             sAttrs = win32security.SECURITY_ATTRIBUTES()
719             StartupInfo = win32process.STARTUPINFO()
720             for var in spawnenv:
721                 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
722             # check for any special operating system commands
723             if cmd == 'del':
724                 for arg in args[1:]:
725                     win32file.DeleteFile(arg)
726                 exit_code = 0
727             else:
728                 # otherwise execute the command.
729                 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
730                 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
731                 exit_code = win32process.GetExitCodeProcess(hProcess)
732                 win32file.CloseHandle(hProcess);
733                 win32file.CloseHandle(hThread);
734         return exit_code
735
736
737 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
738     ''' This function modify env and allow logging of
739         commands to a logfile. If the argument is too long
740         a win32 spawn will be used instead of the system one
741     '''
742     #
743     # create a new spwn object
744     ls = loggedSpawn(env, logfile, longarg, info)
745     # replace the old SPAWN by the new function
746     env['SPAWN'] = ls.spawn
747