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