1 # vi:filetype=python:expandtab:tabstop=4:shiftwidth=4
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
9 # Full author contact details are available in file CREDITS.
11 # This file defines all the utility functions for the
12 # scons-based build system of lyx
15 import os, sys, re, shutil, glob
16 from SCons.Util import *
19 def getVerFromConfigure(path):
20 ''' get lyx version from the AC_INIT line of configure.ac,
21 and LYX_DATE from an AC_SUBST line.
24 config = open(os.path.join(path, 'configure.ac'))
26 print "Can not open configure.ac. "
28 # find a line like follows
29 # AC_INIT(LyX,1.4.4svn,[lyx-devel@lists.lyx.org],[lyx])
30 ver_pat = re.compile('AC_INIT\([^,]+,([^,]+),')
31 date_pat = re.compile('AC_SUBST\(LYX_DATE, \["(.*)"\]\)')
34 for line in config.readlines():
35 if ver_pat.match(line):
36 (version,) = ver_pat.match(line).groups()
37 if date_pat.match(line):
38 (date,) = date_pat.match(line).groups()
39 if version != 'x.x.x' and date != 'Not released':
41 return version.strip(), date.strip()
44 def relativePath(path, base):
45 '''return relative path from base, which is usually top source dir'''
46 # full pathname of path
47 path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
48 path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
49 if path1[:len(path2)] != path2:
50 print "Path %s is not under top source directory" % path
51 if len(path2) == len(path1):
53 path3 = os.path.join(*path1[len(path2):]);
54 # replace all \ by / such that we get the same comments on Windows and *nix
55 path3 = path3.replace('\\', '/')
59 def isSubDir(path, base):
60 '''Whether or not path is a subdirectory of base'''
61 path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
62 path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
63 return len(path2) <= len(path1) and path1[:len(path2)] == path2
66 def writeToFile(filename, lines, append = False):
67 " utility function: write or append lines to filename "
68 # create directory if needed
69 dir = os.path.split(filename)[0]
70 if dir != '' and not os.path.isdir(dir):
73 file = open(filename, 'a')
75 file = open(filename, 'w')
80 def env_subst(target, source, env):
81 ''' subst variables in source by those in env, and output to target
82 source and target are scons File() objects
84 %key% (not key itself) is an indication of substitution
86 assert len(target) == 1
87 assert len(source) == 1
88 target_file = file(str(target[0]), "w")
89 source_file = file(str(source[0]), "r")
91 contents = source_file.read()
92 for k, v in env.items():
94 val = env.subst('$'+k)
95 # temporary fix for the \Resource backslash problem
96 val = val.replace('\\', '/')
97 # multi-line replacement
98 val = val.replace('\n',r'\\n\\\n')
99 contents = re.sub('@'+k+'@', val, contents)
102 target_file.write(contents + "\n")
104 #st = os.stat(str(source[0]))
105 #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
108 def env_nsis(source, target, env, for_signature):
109 ''' Get nsis command line '''
110 def quoteIfSpaced(str):
112 return '"' + str + '"'
115 ret = env['NSIS'] + " /V1 "
116 if env.has_key('NSISFLAGS'):
117 for flag in env['NSISFLAGS']:
120 if env.has_key('NSISDEFINES'):
121 for d in env['NSISDEFINES']:
123 if env['NSISDEFINES'][d]:
124 ret += '=' + quoteIfSpaced(env['NSISDEFINES'][d])
127 if '-bundle.exe' in str(target[0]):
128 ret += '/DSETUPTYPE_BUNDLE=1 '
130 ret += quoteIfSpaced(str(s))
134 def env_toc(target, source, env):
135 '''Generate target from source files'''
136 # this is very tricky because we need to use installed lyx2lyx with
137 # correct lyx2lyx_version.py
138 sys.path.append(env['LYX2LYX_DEST'])
139 sys.path.append(env.Dir('$TOP_SRCDIR/lib/doc').abspath)
142 doc_toc.build_toc(str(target[0]), [file.abspath for file in source])
145 def env_cat(target, source, env):
146 '''Cat source > target. Avoid pipe to increase portability'''
147 output = open(env.File(target[0]).abspath, 'w')
149 input = open(env.File(src).abspath)
150 output.write(input.read())
155 def env_potfiles(target, source, env):
156 '''Build po/POTFILES.in'''
158 # grep -l '_(\".*\")' `find src \( -name '*.h' -o -name '*.cpp' -o -name '*.cpp.in' \) -print` | grep -v -e "src/support/Package.cpp$$" | sort | uniq
159 # is used under *nix but windows users have to do these all in python
160 target_file = open(str(target[0]), "w")
162 trans = re.compile('_\(".*"\)', re.M)
164 rel_file = relativePath(str(file), env.subst('$TOP_SRCDIR'))
165 if rel_file not in potfiles and trans.search(open(str(file)).read()):
166 potfiles.append(rel_file)
168 print >> target_file, '\n'.join(potfiles)
172 def createResFromIcon(env, icon_file, rc_file):
173 ''' create a rc file with icon, and return res file (windows only) '''
175 rc_name = env.File(rc_file).abspath
176 dir = os.path.split(rc_name)[0]
177 if not os.path.isdir(dir):
179 rc = open(rc_name, 'w')
180 print >> rc, 'IDI_ICON1 ICON DISCARDABLE "%s"' % \
181 os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'development', 'win32',
182 'packaging', 'icons', icon_file).replace('\\', '\\\\')
184 return env.RES(rc_name)
193 def checkPkgConfig(conf, version):
194 ''' Return false if pkg_config does not exist, or is too old '''
195 conf.Message('Checking for pkg-config...')
196 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
201 def checkPackage(conf, pkg):
202 ''' check if pkg is under the control of conf '''
203 conf.Message('Checking for package %s...' % pkg)
204 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
209 def checkMkdirOneArg(conf):
210 check_mkdir_one_arg_source = """
211 #include <sys/stat.h>
217 conf.Message('Checking for the number of args for mkdir... ')
218 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
219 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
220 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
228 def checkCXXGlobalCstd(conf):
229 ''' Checking the use of std::tolower or tolower '''
230 check_global_cstd_source = '''
238 conf.Message('Checking for the use of global cstd... ')
239 # if can not compile, define CXX_GLOBAL_CSTD
240 ret = conf.TryLink(check_global_cstd_source, '.cpp')
245 def checkSelectArgType(conf):
246 ''' Adapted from autoconf '''
247 conf.Message('Checking for arg types for select... ')
248 for arg234 in ['fd_set *', 'int *', 'void *']:
249 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
250 for arg5 in ['struct timeval *', 'const struct timeval *']:
251 check_select_source = '''
252 #if HAVE_SYS_SELECT_H
253 # include <sys/select.h>
255 #if HAVE_SYS_SOCKET_H
256 # include <sys/socket.h>
258 extern int select (%s, %s, %s, %s, %s);
263 ''' % (arg1, arg234, arg234, arg234, arg5)
264 ret = conf.TryLink(check_select_source, '.c')
267 return (arg1, arg234, arg5)
268 conf.Result('no (use default)')
269 return ('int', 'int *', 'struct timeval *')
272 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
273 ''' look for boost libraries
275 lib_paths: try these paths for boost libraries
276 inc_paths: try these paths for boost headers
277 versions: supported boost versions
278 isDebug: if true, use debug libraries
280 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
281 libprefix = conf.env['LIBPREFIX']
282 libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
288 for path in lib_paths:
289 conf.Log("Looking into %s\n" % path)
291 # get all the libs, then filter for the right library
292 files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
293 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
295 conf.Log("Find boost libraries: %s\n" % files)
296 # runtime code includes s,g,y,d,p,n, where we should look for
297 # d,g,y for debug, s,p,n for release
301 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
304 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
305 if len(lib_files) == 0:
306 # use alternative libraries
308 lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
309 if len(lib_files) > 0:
310 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
311 name = lib_files[0].split(os.sep)[-1][len(libprefix):]
312 lib_names.append(name.split('.')[0])
313 conf.Log("Qualified libraries: %s\n" % lib_names)
315 conf.Log("No qualified library is found.\n")
317 if len(lib_names) == len(libs):
322 if len(lib_names) == 0:
323 conf.Log("No boost library is found\n")
325 conf.Log("Found boost libraries: %s\n" % lib_names)
327 return (None, None, None)
328 # check version number in boost/version.hpp
329 def isValidBoostDir(dir):
330 version_file = os.path.join(dir, 'boost', 'version.hpp')
331 if not os.path.isfile(version_file):
333 version_file_content = open(version_file).read()
334 version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
335 return True in [x in version_file_content for x in version_strings]
336 # check for boost header file
337 for path in inc_paths:
338 conf.Log("Checking for inc path: %s\n" % path)
339 if isValidBoostDir(path):
342 else: # check path/boost_1_xx_x/boost
343 dirs = glob.glob(os.path.join(path, 'boost-*'))
344 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
345 conf.Log("Checing for sub directory: %s\n" % dirs[0])
351 conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
352 return (lib_names, lib_path, inc_path)
355 return (None, None, None)
358 def checkCommand(conf, cmd):
359 ''' check the existence of a command
360 return full path to the command, or none
362 conf.Message('Checking for command %s...' % cmd)
364 conf.Result(res is not None)
369 ''' check the existence of nsis compiler, return the fullpath '''
370 conf.Message('Checking for nsis compiler...')
373 # If we can read the registry, get the NSIS command from it
375 k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
377 val, tok = RegQueryValueEx(k,None)
378 ret = val + os.path.sep + 'makensis.exe'
379 if os.path.isfile(ret):
380 res = '"' + ret + '"'
384 pass # Couldn't find the key, just act like we can't read the registry
385 # Hope it's on the path
387 res = WhereIs('makensis.exe')
388 conf.Result(res is not None)
392 def checkLC_MESSAGES(conf):
393 ''' check the definition of LC_MESSAGES '''
394 check_LC_MESSAGES = '''
401 conf.Message('Checking for LC_MESSAGES in locale.h... ')
402 ret = conf.TryLink(check_LC_MESSAGES, '.c')
407 def checkIconvConst(conf):
408 ''' check the declaration of iconv '''
409 check_iconv_const = '''
411 // this declaration will fail when there already exists a non const char**
412 // version which returns size_t
413 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
418 conf.Message('Checking if the declaration of iconv needs const... ')
419 ret = conf.TryLink(check_iconv_const, '.cpp')
424 def checkSizeOfWChar(conf):
425 ''' check the size of wchar '''
426 check_sizeof_wchar = '''
427 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
433 conf.Message('Checking the size of wchar_t... ')
434 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
436 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
440 conf.Result(str(ret))
444 def checkDeclaration(conf, func, headers):
445 ''' check if a function is declared in given headers '''
451 char *p = (char *) %s;
455 conf.Message('Checking for the declaration of function %s... ' % func)
456 ret = True in [conf.TryLink(check_decl % header, '.c') for header in headers]
461 def createConfigFile(conf, config_file,
462 config_pre = '', config_post = '',
463 headers = [], functions = [], declarations = [], types = [], libs = [],
464 custom_tests = [], extra_items = []):
465 ''' create a configuration file, with options
466 config_file: which file to create
467 config_pre: first part of the config file
468 config_post: last part of the config file
469 headers: header files to check, in the form of a list of
470 ('file', 'HAVE_FILE', 'c'/'c++')
471 functions: functions to check, in the form of a list of
472 ('func', 'HAVE_func', 'include lines'/None)
473 declarations: function declarations to check, in the form of a list of
474 ('func', 'HAVE_DECL_func', header_files)
475 types: types to check, in the form of a list of
476 ('type', 'HAVE_TYPE', 'includelines'/None)
477 libs: libraries to check, in the form of a list of
478 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
479 or any of the libs exists if 'lib' is a list of libs.
480 Optionally, user can provide another key LIB_NAME, that will
481 be set to the detected lib (or None otherwise).
482 custom_tests: extra tests to perform, in the form of a list of
483 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
484 If the last two are ignored, '#define key 1' '/*#undef key */'
486 extra_items: extra configuration lines, in the form of a list of
487 ('config', 'description')
489 The result of each test, as a dictioanry of
490 res['XXX'] = True/False
491 XXX are keys defined in each argument.
493 cont = config_pre + '\n'
495 # add to this string, in appropriate format
496 def configString(lines, desc=''):
498 if lines.strip() != '':
500 text += '/* ' + desc + ' */\n'
501 text += lines + '\n\n'
505 for header in headers:
506 description = "Define to 1 if you have the <%s> header file." % header[0]
507 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
508 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
509 result[header[1]] = 1
510 cont += configString('#define %s 1' % header[1], desc = description)
512 result[header[1]] = 0
513 cont += configString('/* #undef %s */' % header[1], desc = description)
515 for func in functions:
516 description = "Define to 1 if you have the `%s' function." % func[0]
517 if conf.CheckFunc(func[0], header=func[2]):
519 cont += configString('#define %s 1' % func[1], desc = description)
522 cont += configString('/* #undef %s */' % func[1], desc = description)
523 for decl in declarations:
524 description = "Define to 1 if you have the declaration of `%s', and to 0 if you don't." % decl[0]
525 if conf.CheckDeclaration(decl[0], decl[2]):
527 cont += configString('#define %s 1' % decl[1], desc = description)
530 cont += configString('/* #undef %s */' % decl[1], desc = description)
533 description = "Define to 1 if you have the `%s' type." % t[0]
534 if conf.CheckType(t[0], includes=t[2]):
536 cont += configString('#define %s 1' % t[1], desc = description)
539 cont += configString('/* #undef %s */' % t[1], desc = description)
542 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
543 if type(lib[0]) is type(''):
547 # check if any of the lib exists
549 # if user want the name of the lib detected
551 result[lib[2]] = None
553 if conf.CheckLib(ll):
557 cont += configString('#define %s 1' % lib[1], desc = description)
560 if not result[lib[1]]:
561 cont += configString('/* #undef %s */' % lib[1], desc = description)
563 for test in custom_tests:
567 cont += configString('#define %s 1' % test[1], desc = test[2])
569 cont += configString(test[3], desc = test[2])
573 cont += configString('/* #undef %s */' % test[1], desc = test[2])
575 cont += configString(test[4], desc = test[2])
576 # extra items (no key is returned)
577 for item in extra_items:
578 cont += configString(item[0], desc = item[1])
580 cont += '\n' + config_post + '\n'
582 writeToFile(config_file, cont)
586 def installCygwinLDScript(path):
587 ''' Install i386pe.x-no-rdata '''
588 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
589 script = open(ld_script, 'w')
590 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
592 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
593 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
595 OUTPUT_FORMAT(pei-i386)
596 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
597 ENTRY(_mainCRTStartup)
600 .text __image_base__ + __section_alignment__ :
607 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
608 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
609 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
610 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
612 /* ??? Why is .gcc_exc here? */
617 /* The Cygwin32 library uses a section to avoid copying certain data
618 on fork. This used to be named ".data". The linker used
619 to include this between __data_start__ and __data_end__, but that
620 breaks building the cygwin32 dll. Instead, we name the section
621 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
622 .data BLOCK(__section_alignment__) :
631 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
632 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
633 *(.rdata_runtime_pseudo_reloc)
634 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
635 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
637 *(.data_cygwin_nocopy)
639 .rdata BLOCK(__section_alignment__) :
642 .pdata BLOCK(__section_alignment__) :
646 .bss BLOCK(__section_alignment__) :
653 .edata BLOCK(__section_alignment__) :
664 .idata BLOCK(__section_alignment__) :
666 /* This cannot currently be handled with grouped sections.
667 See pe.em:sort_sections. */
670 /* These zeroes mark the end of the import list. */
671 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
677 .CRT BLOCK(__section_alignment__) :
679 ___crt_xc_start__ = . ;
680 *(SORT(.CRT$XC*)) /* C initialization */
681 ___crt_xc_end__ = . ;
682 ___crt_xi_start__ = . ;
683 *(SORT(.CRT$XI*)) /* C++ initialization */
684 ___crt_xi_end__ = . ;
685 ___crt_xl_start__ = . ;
686 *(SORT(.CRT$XL*)) /* TLS callbacks */
687 /* ___crt_xl_end__ is defined in the TLS Directory support code */
688 ___crt_xp_start__ = . ;
689 *(SORT(.CRT$XP*)) /* Pre-termination */
690 ___crt_xp_end__ = . ;
691 ___crt_xt_start__ = . ;
692 *(SORT(.CRT$XT*)) /* Termination */
693 ___crt_xt_end__ = . ;
695 .tls BLOCK(__section_alignment__) :
703 .endjunk BLOCK(__section_alignment__) :
705 /* end is deprecated, don't use it */
710 .rsrc BLOCK(__section_alignment__) :
715 .reloc BLOCK(__section_alignment__) :
719 .stab BLOCK(__section_alignment__) (NOLOAD) :
723 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
727 /* DWARF debug sections.
728 Symbols in the DWARF debugging sections are relative to the beginning
729 of the section. Unlike other targets that fake this by putting the
730 section VMA at 0, the PE format will not allow it. */
731 /* DWARF 1.1 and DWARF 2. */
732 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
736 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
741 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
743 *(.debug_info) *(.gnu.linkonce.wi.*)
745 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
749 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
753 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
757 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
761 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
765 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
769 /* SGI/MIPS DWARF 2 extensions. */
770 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
774 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
778 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
782 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
787 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
797 def installCygwinPostinstallScript(path):
798 ''' Install lyx.sh '''
799 postinstall_script = os.path.join(path, 'lyx.sh')
800 script = open(postinstall_script, 'w')
801 script.write(r'''#!/bin/sh
803 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
804 # if it is not already there.
805 if [ -f /etc/fonts/local.conf ]; then
806 grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
807 if [ $? -ne 0 ]; then
808 sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
809 mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
810 fc-cache /usr/share/lyx/fonts
815 return(postinstall_script)
819 # these will be used under win32
825 # does not matter if it fails on other systems
830 def __init__(self, env, logfile, longarg, info):
831 # save the spawn system
833 self.logfile = logfile
834 # clear the logfile (it may not exist)
836 # this will overwrite existing content.
837 writeToFile(logfile, info, append=False)
839 self.longarg = longarg
840 # get hold of the old spawn? (necessary?)
841 self._spawn = env['SPAWN']
844 def spawn(self, sh, escape, cmd, args, spawnenv):
846 newargs = ' '.join(map(escape, args[1:]))
847 cmdline = cmd + " " + newargs
849 # if log is not empty, write to it
850 if self.logfile != '':
851 # this tend to be slow (?) but ensure correct output
852 # Note that cmdline may be long so I do not escape it
854 # since this is not an essential operation, proceed if things go wrong here.
855 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
857 print "Warning: can not write to log file ", self.logfile
859 # if the command is not too long, use the old
860 if not self.longarg or len(cmdline) < 8000:
861 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
863 sAttrs = win32security.SECURITY_ATTRIBUTES()
864 StartupInfo = win32process.STARTUPINFO()
866 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
867 # check for any special operating system commands
870 win32file.DeleteFile(arg)
873 # otherwise execute the command.
874 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
875 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
876 exit_code = win32process.GetExitCodeProcess(hProcess)
877 win32file.CloseHandle(hProcess);
878 win32file.CloseHandle(hThread);
882 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
883 ''' This function modify env and allow logging of
884 commands to a logfile. If the argument is too long
885 a win32 spawn will be used instead of the system one
888 # create a new spwn object
889 ls = loggedSpawn(env, logfile, longarg, info)
890 # replace the old SPAWN by the new function
891 env['SPAWN'] = ls.spawn