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