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