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