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 ret = conf.TryLink(check_global_cstd_source, '.c')
244 def checkSelectArgType(conf):
245 ''' Adapted from autoconf '''
246 conf.Message('Checking for arg types for select... ')
247 for arg234 in ['fd_set *', 'int *', 'void *']:
248 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
249 for arg5 in ['struct timeval *', 'const struct timeval *']:
250 check_select_source = '''
251 #if HAVE_SYS_SELECT_H
252 # include <sys/select.h>
254 #if HAVE_SYS_SOCKET_H
255 # include <sys/socket.h>
257 extern int select (%s, %s, %s, %s, %s);
262 ''' % (arg1, arg234, arg234, arg234, arg5)
263 ret = conf.TryLink(check_select_source, '.c')
266 return (arg1, arg234, arg5)
267 conf.Result('no (use default)')
268 return ('int', 'int *', 'struct timeval *')
271 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
272 ''' look for boost libraries
274 lib_paths: try these paths for boost libraries
275 inc_paths: try these paths for boost headers
276 versions: supported boost versions
277 isDebug: if true, use debug libraries
279 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
280 libprefix = conf.env['LIBPREFIX']
281 libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
287 for path in lib_paths:
288 conf.Log("Looking into %s\n" % path)
290 # get all the libs, then filter for the right library
291 files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
292 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
294 conf.Log("Find boost libraries: %s\n" % files)
295 # runtime code includes s,g,y,d,p,n, where we should look for
296 # d,g,y for debug, s,p,n for release
300 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
303 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
304 if len(lib_files) == 0:
305 # use alternative libraries
307 lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
308 if len(lib_files) > 0:
309 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
310 name = lib_files[0].split(os.sep)[-1][len(libprefix):]
311 lib_names.append(name.split('.')[0])
312 conf.Log("Qualified libraries: %s\n" % lib_names)
314 conf.Log("No qualified library is found.\n")
316 if len(lib_names) == len(libs):
321 if len(lib_names) == 0:
322 conf.Log("No boost library is found\n")
324 conf.Log("Found boost libraries: %s\n" % lib_names)
326 return (None, None, None)
327 # check version number in boost/version.hpp
328 def isValidBoostDir(dir):
329 version_file = os.path.join(dir, 'boost', 'version.hpp')
330 if not os.path.isfile(version_file):
332 version_file_content = open(version_file).read()
333 version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
334 return True in [x in version_file_content for x in version_strings]
335 # check for boost header file
336 for path in inc_paths:
337 conf.Log("Checking for inc path: %s\n" % path)
338 if isValidBoostDir(path):
341 else: # check path/boost_1_xx_x/boost
342 dirs = glob.glob(os.path.join(path, 'boost-*'))
343 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
344 conf.Log("Checing for sub directory: %s\n" % dirs[0])
350 conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
351 return (lib_names, lib_path, inc_path)
354 return (None, None, None)
357 def checkCommand(conf, cmd):
358 ''' check the existence of a command
359 return full path to the command, or none
361 conf.Message('Checking for command %s...' % cmd)
363 conf.Result(res is not None)
368 ''' check the existence of nsis compiler, return the fullpath '''
369 conf.Message('Checking for nsis compiler...')
372 # If we can read the registry, get the NSIS command from it
374 k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
376 val, tok = RegQueryValueEx(k,None)
377 ret = val + os.path.sep + 'makensis.exe'
378 if os.path.isfile(ret):
379 res = '"' + ret + '"'
383 pass # Couldn't find the key, just act like we can't read the registry
384 # Hope it's on the path
386 res = WhereIs('makensis.exe')
387 conf.Result(res is not None)
391 def checkLC_MESSAGES(conf):
392 ''' check the definition of LC_MESSAGES '''
393 check_LC_MESSAGES = '''
400 conf.Message('Checking for LC_MESSAGES in locale.h... ')
401 ret = conf.TryLink(check_LC_MESSAGES, '.c')
406 def checkIconvConst(conf):
407 ''' check the declaration of iconv '''
408 check_iconv_const = '''
410 // this declaration will fail when there already exists a non const char**
411 // version which returns size_t
412 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
417 conf.Message('Checking if the declaration of iconv needs const... ')
418 ret = conf.TryLink(check_iconv_const, '.c')
423 def checkSizeOfWChar(conf):
424 ''' check the size of wchar '''
425 check_sizeof_wchar = '''
426 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
432 conf.Message('Checking the size of wchar_t... ')
433 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
435 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
439 conf.Result(str(ret))
443 def checkDeclaration(conf, func, headers):
444 ''' check if a function is declared in given headers '''
450 char *p = (char *) %s;
454 conf.Message('Checking for the declaration of function %s... ' % func)
455 ret = True in [conf.TryLink(check_decl % header, '.c') for header in headers]
460 def createConfigFile(conf, config_file,
461 config_pre = '', config_post = '',
462 headers = [], functions = [], declarations = [], types = [], libs = [],
463 custom_tests = [], extra_items = []):
464 ''' create a configuration file, with options
465 config_file: which file to create
466 config_pre: first part of the config file
467 config_post: last part of the config file
468 headers: header files to check, in the form of a list of
469 ('file', 'HAVE_FILE', 'c'/'c++')
470 functions: functions to check, in the form of a list of
471 ('func', 'HAVE_func', 'include lines'/None)
472 declarations: function declarations to check, in the form of a list of
473 ('func', 'HAVE_DECL_func', header_files)
474 types: types to check, in the form of a list of
475 ('type', 'HAVE_TYPE', 'includelines'/None)
476 libs: libraries to check, in the form of a list of
477 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
478 or any of the libs exists if 'lib' is a list of libs.
479 Optionally, user can provide another key LIB_NAME, that will
480 be set to the detected lib (or None otherwise).
481 custom_tests: extra tests to perform, in the form of a list of
482 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
483 If the last two are ignored, '#define key 1' '/*#undef key */'
485 extra_items: extra configuration lines, in the form of a list of
486 ('config', 'description')
488 The result of each test, as a dictioanry of
489 res['XXX'] = True/False
490 XXX are keys defined in each argument.
492 cont = config_pre + '\n'
494 # add to this string, in appropriate format
495 def configString(lines, desc=''):
497 if lines.strip() != '':
499 text += '/* ' + desc + ' */\n'
500 text += lines + '\n\n'
504 for header in headers:
505 description = "Define to 1 if you have the <%s> header file." % header[0]
506 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
507 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
508 result[header[1]] = 1
509 cont += configString('#define %s 1' % header[1], desc = description)
511 result[header[1]] = 0
512 cont += configString('/* #undef %s */' % header[1], desc = description)
514 for func in functions:
515 description = "Define to 1 if you have the `%s' function." % func[0]
516 if conf.CheckFunc(func[0], header=func[2]):
518 cont += configString('#define %s 1' % func[1], desc = description)
521 cont += configString('/* #undef %s */' % func[1], desc = description)
522 for decl in declarations:
523 description = "Define to 1 if you have the declaration of `%s', and to 0 if you don't." % decl[0]
524 if conf.CheckDeclaration(decl[0], decl[2]):
526 cont += configString('#define %s 1' % decl[1], desc = description)
529 cont += configString('/* #undef %s */' % decl[1], desc = description)
532 description = "Define to 1 if you have the `%s' type." % t[0]
533 if conf.CheckType(t[0], includes=t[2]):
535 cont += configString('#define %s 1' % t[1], desc = description)
538 cont += configString('/* #undef %s */' % t[1], desc = description)
541 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
542 if type(lib[0]) is type(''):
546 # check if any of the lib exists
548 # if user want the name of the lib detected
550 result[lib[2]] = None
552 if conf.CheckLib(ll):
556 cont += configString('#define %s 1' % lib[1], desc = description)
559 if not result[lib[1]]:
560 cont += configString('/* #undef %s */' % lib[1], desc = description)
562 for test in custom_tests:
566 cont += configString('#define %s 1' % test[1], desc = test[2])
568 cont += configString(test[3], desc = test[2])
572 cont += configString('/* #undef %s */' % test[1], desc = test[2])
574 cont += configString(test[4], desc = test[2])
575 # extra items (no key is returned)
576 for item in extra_items:
577 cont += configString(item[0], desc = item[1])
579 cont += '\n' + config_post + '\n'
581 writeToFile(config_file, cont)
585 def installCygwinLDScript(path):
586 ''' Install i386pe.x-no-rdata '''
587 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
588 script = open(ld_script, 'w')
589 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
591 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
592 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
594 OUTPUT_FORMAT(pei-i386)
595 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
596 ENTRY(_mainCRTStartup)
599 .text __image_base__ + __section_alignment__ :
606 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
607 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
608 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
609 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
611 /* ??? Why is .gcc_exc here? */
616 /* The Cygwin32 library uses a section to avoid copying certain data
617 on fork. This used to be named ".data". The linker used
618 to include this between __data_start__ and __data_end__, but that
619 breaks building the cygwin32 dll. Instead, we name the section
620 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
621 .data BLOCK(__section_alignment__) :
630 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
631 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
632 *(.rdata_runtime_pseudo_reloc)
633 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
634 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
636 *(.data_cygwin_nocopy)
638 .rdata BLOCK(__section_alignment__) :
641 .pdata BLOCK(__section_alignment__) :
645 .bss BLOCK(__section_alignment__) :
652 .edata BLOCK(__section_alignment__) :
663 .idata BLOCK(__section_alignment__) :
665 /* This cannot currently be handled with grouped sections.
666 See pe.em:sort_sections. */
669 /* These zeroes mark the end of the import list. */
670 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
676 .CRT BLOCK(__section_alignment__) :
678 ___crt_xc_start__ = . ;
679 *(SORT(.CRT$XC*)) /* C initialization */
680 ___crt_xc_end__ = . ;
681 ___crt_xi_start__ = . ;
682 *(SORT(.CRT$XI*)) /* C++ initialization */
683 ___crt_xi_end__ = . ;
684 ___crt_xl_start__ = . ;
685 *(SORT(.CRT$XL*)) /* TLS callbacks */
686 /* ___crt_xl_end__ is defined in the TLS Directory support code */
687 ___crt_xp_start__ = . ;
688 *(SORT(.CRT$XP*)) /* Pre-termination */
689 ___crt_xp_end__ = . ;
690 ___crt_xt_start__ = . ;
691 *(SORT(.CRT$XT*)) /* Termination */
692 ___crt_xt_end__ = . ;
694 .tls BLOCK(__section_alignment__) :
702 .endjunk BLOCK(__section_alignment__) :
704 /* end is deprecated, don't use it */
709 .rsrc BLOCK(__section_alignment__) :
714 .reloc BLOCK(__section_alignment__) :
718 .stab BLOCK(__section_alignment__) (NOLOAD) :
722 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
726 /* DWARF debug sections.
727 Symbols in the DWARF debugging sections are relative to the beginning
728 of the section. Unlike other targets that fake this by putting the
729 section VMA at 0, the PE format will not allow it. */
730 /* DWARF 1.1 and DWARF 2. */
731 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
735 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
740 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
742 *(.debug_info) *(.gnu.linkonce.wi.*)
744 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
748 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
752 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
756 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
760 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
764 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
768 /* SGI/MIPS DWARF 2 extensions. */
769 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
773 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
777 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
781 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
786 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
796 def installCygwinPostinstallScript(path):
797 ''' Install lyx.sh '''
798 postinstall_script = os.path.join(path, 'lyx.sh')
799 script = open(postinstall_script, 'w')
800 script.write(r'''#!/bin/sh
802 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
803 # if it is not already there.
804 if [ -f /etc/fonts/local.conf ]; then
805 grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
806 if [ $? -ne 0 ]; then
807 sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
808 mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
809 fc-cache /usr/share/lyx/fonts
814 return(postinstall_script)
818 # these will be used under win32
824 # does not matter if it fails on other systems
829 def __init__(self, env, logfile, longarg, info):
830 # save the spawn system
832 self.logfile = logfile
833 # clear the logfile (it may not exist)
835 # this will overwrite existing content.
836 writeToFile(logfile, info, append=False)
838 self.longarg = longarg
839 # get hold of the old spawn? (necessary?)
840 self._spawn = env['SPAWN']
843 def spawn(self, sh, escape, cmd, args, spawnenv):
845 newargs = ' '.join(map(escape, args[1:]))
846 cmdline = cmd + " " + newargs
848 # if log is not empty, write to it
849 if self.logfile != '':
850 # this tend to be slow (?) but ensure correct output
851 # Note that cmdline may be long so I do not escape it
853 # since this is not an essential operation, proceed if things go wrong here.
854 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
856 print "Warning: can not write to log file ", self.logfile
858 # if the command is not too long, use the old
859 if not self.longarg or len(cmdline) < 8000:
860 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
862 sAttrs = win32security.SECURITY_ATTRIBUTES()
863 StartupInfo = win32process.STARTUPINFO()
865 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
866 # check for any special operating system commands
869 win32file.DeleteFile(arg)
872 # otherwise execute the command.
873 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
874 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
875 exit_code = win32process.GetExitCodeProcess(hProcess)
876 win32file.CloseHandle(hProcess);
877 win32file.CloseHandle(hThread);
881 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
882 ''' This function modify env and allow logging of
883 commands to a logfile. If the argument is too long
884 a win32 spawn will be used instead of the system one
887 # create a new spwn object
888 ls = loggedSpawn(env, logfile, longarg, info)
889 # replace the old SPAWN by the new function
890 env['SPAWN'] = ls.spawn