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