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