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 packed major and minor version numbers from the lyx version,
22 and LYX_DATE from an AC_SUBST line.
25 config = open(os.path.join(path, 'configure.ac'))
27 print "Can not open configure.ac. "
29 # find a line like follows
30 # AC_INIT(LyX,1.4.4svn,[lyx-devel@lists.lyx.org],[lyx])
31 ver_pat = re.compile('AC_INIT\([^,]+,([^,]+),')
32 date_pat = re.compile('AC_SUBST\(LYX_DATE, \["(.*)"\]\)')
33 majmin_pat = re.compile('(\d+)\.(\d+)\..*')
37 for line in config.readlines():
38 if ver_pat.match(line):
39 (version,) = ver_pat.match(line).groups()
40 majmin_match = majmin_pat.match(version)
41 majmin = majmin_match.group(1) + majmin_match.group(2)
42 if date_pat.match(line):
43 (date,) = date_pat.match(line).groups()
44 if version != 'x.x.x' and date != 'Not released':
46 return version.strip(), majmin.strip(), date.strip()
49 def relativePath(path, base):
50 '''return relative path from base, which is usually top source dir'''
51 # full pathname of path
52 path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
53 path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
54 if path1[:len(path2)] != path2:
55 print "Path %s is not under top source directory" % path
56 if len(path2) == len(path1):
58 path3 = os.path.join(*path1[len(path2):]);
59 # replace all \ by / such that we get the same comments on Windows and *nix
60 path3 = path3.replace('\\', '/')
64 def isSubDir(path, base):
65 '''Whether or not path is a subdirectory of base'''
66 path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
67 path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
68 return len(path2) <= len(path1) and path1[:len(path2)] == path2
71 def writeToFile(filename, lines, append = False):
72 " utility function: write or append lines to filename "
73 # create directory if needed
74 dir = os.path.split(filename)[0]
75 if dir != '' and not os.path.isdir(dir):
78 file = open(filename, 'a')
80 file = open(filename, 'w')
85 def env_subst(target, source, env):
86 ''' subst variables in source by those in env, and output to target
87 source and target are scons File() objects
89 %key% (not key itself) is an indication of substitution
91 assert len(target) == 1
92 assert len(source) == 1
93 target_file = file(str(target[0]), "w")
94 source_file = file(str(source[0]), "r")
96 contents = source_file.read()
97 for k, v in env.items():
99 val = env.subst('$'+k)
100 # temporary fix for the \Resource backslash problem
101 val = val.replace('\\', '/')
102 # multi-line replacement
103 val = val.replace('\n',r'\\n\\\n')
104 contents = re.sub('@'+k+'@', val, contents)
107 target_file.write(contents + "\n")
109 #st = os.stat(str(source[0]))
110 #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
113 def env_nsis(source, target, env, for_signature):
114 ''' Get nsis command line '''
115 def quoteIfSpaced(str):
117 return '"' + str + '"'
120 ret = env['NSIS'] + " /V1 "
121 if env.has_key('NSISFLAGS'):
122 for flag in env['NSISFLAGS']:
125 if env.has_key('NSISDEFINES'):
126 for d in env['NSISDEFINES']:
128 if env['NSISDEFINES'][d]:
129 ret += '=' + quoteIfSpaced(env['NSISDEFINES'][d])
132 if '-bundle.exe' in str(target[0]):
133 ret += '/DSETUPTYPE=BUNDLE '
135 ret += quoteIfSpaced(str(s))
139 def env_toc(target, source, env):
140 '''Generate target from source files'''
141 # this is very tricky because we need to use installed lyx2lyx with
142 # correct lyx2lyx_version.py
143 sys.path.append(env['LYX2LYX_DEST'])
144 sys.path.append(env.Dir('$TOP_SRCDIR/lib/doc').abspath)
147 doc_toc.build_toc(str(target[0]), [file.abspath for file in source])
150 def env_cat(target, source, env):
151 '''Cat source > target. Avoid pipe to increase portability'''
152 output = open(env.File(target[0]).abspath, 'w')
154 input = open(env.File(src).abspath)
155 output.write(input.read())
160 def env_potfiles(target, source, env):
161 '''Build po/POTFILES.in'''
163 # grep -l '_(\".*\")' `find src \( -name '*.h' -o -name '*.cpp' -o -name '*.cpp.in' \) -print` | grep -v -e "src/support/Package.cpp$$" | sort | uniq
164 # is used under *nix but windows users have to do these all in python
165 target_file = open(str(target[0]), "w")
167 trans = re.compile('_\(".*"\)', re.M)
169 rel_file = relativePath(str(file), env.subst('$TOP_SRCDIR'))
170 if rel_file not in potfiles and trans.search(open(str(file)).read()):
171 potfiles.append(rel_file)
173 print >> target_file, '\n'.join(potfiles)
177 def createResFromIcon(env, icon_file, rc_file):
178 ''' create a rc file with icon, and return res file (windows only) '''
180 rc_name = env.File(rc_file).abspath
181 dir = os.path.split(rc_name)[0]
182 if not os.path.isdir(dir):
184 rc = open(rc_name, 'w')
185 print >> rc, 'IDI_ICON1 ICON DISCARDABLE "%s"' % \
186 os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'development', 'win32',
187 'packaging', 'icons', icon_file).replace('\\', '\\\\')
189 return env.RES(rc_name)
194 def env_qtResource(target, source, env):
195 '''Create resource.qrc'''
196 qrc = open(str(target[0]), 'w')
197 print >> qrc, "<!DOCTYPE RCC><RCC version='1.0'><qresource>"
199 rel_file = relativePath(str(file), env.subst('$TOP_SRCDIR/lib'))
200 abs_file = str(file.abspath)
201 print >> qrc, '<file alias="%s">%s</file>' % (rel_file, abs_file)
202 print >> qrc, '</qresource></RCC>'
209 def checkPkgConfig(conf, version):
210 ''' Return false if pkg_config does not exist, or is too old '''
211 conf.Message('Checking for pkg-config...')
212 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
217 def checkPackage(conf, pkg):
218 ''' check if pkg is under the control of conf '''
219 conf.Message('Checking for package %s...' % pkg)
220 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
225 def checkMkdirOneArg(conf):
226 check_mkdir_one_arg_source = """
227 #include <sys/stat.h>
233 conf.Message('Checking for the number of args for mkdir... ')
234 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
235 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
236 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
244 def checkCXXGlobalCstd(conf):
245 ''' Checking the use of std::tolower or tolower '''
246 check_global_cstd_source = '''
254 conf.Message('Checking for the use of global cstd... ')
255 # if can not compile, define CXX_GLOBAL_CSTD
256 ret = conf.TryLink(check_global_cstd_source, '.cpp')
261 def checkSelectArgType(conf):
262 ''' Adapted from autoconf '''
263 conf.Message('Checking for arg types for select... ')
264 for arg234 in ['fd_set *', 'int *', 'void *']:
265 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
266 for arg5 in ['struct timeval *', 'const struct timeval *']:
267 check_select_source = '''
268 #if HAVE_SYS_SELECT_H
269 # include <sys/select.h>
271 #if HAVE_SYS_SOCKET_H
272 # include <sys/socket.h>
274 extern int select (%s, %s, %s, %s, %s);
279 ''' % (arg1, arg234, arg234, arg234, arg5)
280 ret = conf.TryLink(check_select_source, '.c')
283 return (arg1, arg234, arg5)
284 conf.Result('no (use default)')
285 return ('int', 'int *', 'struct timeval *')
288 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
289 ''' look for boost libraries
291 lib_paths: try these paths for boost libraries
292 inc_paths: try these paths for boost headers
293 versions: supported boost versions
294 isDebug: if true, use debug libraries
296 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
297 libprefix = conf.env['LIBPREFIX']
298 libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
304 for path in lib_paths:
305 conf.Log("Looking into %s\n" % path)
307 # get all the libs, then filter for the right library
308 files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
309 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
311 conf.Log("Find boost libraries: %s\n" % files)
312 # runtime code includes s,g,y,d,p,n, where we should look for
313 # d,g,y for debug, s,p,n for release
317 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
320 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
321 if len(lib_files) == 0:
322 # use alternative libraries
324 lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
325 if len(lib_files) > 0:
326 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
327 name = lib_files[0].split(os.sep)[-1][len(libprefix):]
328 lib_names.append(name.split('.')[0])
329 conf.Log("Qualified libraries: %s\n" % lib_names)
331 conf.Log("No qualified library is found.\n")
333 if len(lib_names) == len(libs):
338 if len(lib_names) == 0:
339 conf.Log("No boost library is found\n")
341 conf.Log("Found boost libraries: %s\n" % lib_names)
343 return (None, None, None)
344 # check version number in boost/version.hpp
345 def isValidBoostDir(dir):
346 version_file = os.path.join(dir, 'boost', 'version.hpp')
347 if not os.path.isfile(version_file):
349 version_file_content = open(version_file).read()
350 version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
351 return True in [x in version_file_content for x in version_strings]
352 # check for boost header file
353 for path in inc_paths:
354 conf.Log("Checking for inc path: %s\n" % path)
355 if isValidBoostDir(path):
358 else: # check path/boost_1_xx_x/boost
359 dirs = glob.glob(os.path.join(path, 'boost-*'))
360 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
361 conf.Log("Checing for sub directory: %s\n" % dirs[0])
367 conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
368 return (lib_names, lib_path, inc_path)
371 return (None, None, None)
374 def checkCommand(conf, cmd):
375 ''' check the existence of a command
376 return full path to the command, or none
378 conf.Message('Checking for command %s...' % cmd)
380 conf.Result(res is not None)
385 ''' check the existence of nsis compiler, return the fullpath '''
386 conf.Message('Checking for nsis compiler...')
389 # If we can read the registry, get the NSIS command from it
391 k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
393 val, tok = RegQueryValueEx(k,None)
394 ret = val + os.path.sep + 'makensis.exe'
395 if os.path.isfile(ret):
396 res = '"' + ret + '"'
400 pass # Couldn't find the key, just act like we can't read the registry
401 # Hope it's on the path
403 res = WhereIs('makensis.exe')
404 conf.Result(res is not None)
408 def checkLC_MESSAGES(conf):
409 ''' check the definition of LC_MESSAGES '''
410 check_LC_MESSAGES = '''
417 conf.Message('Checking for LC_MESSAGES in locale.h... ')
418 ret = conf.TryLink(check_LC_MESSAGES, '.c')
423 def checkIconvConst(conf):
424 ''' check the declaration of iconv '''
425 check_iconv_const = '''
427 // this declaration will fail when there already exists a non const char**
428 // version which returns size_t
429 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
434 conf.Message('Checking if the declaration of iconv needs const... ')
435 ret = conf.TryLink(check_iconv_const, '.cpp')
440 def checkSizeOfWChar(conf):
441 ''' check the size of wchar '''
442 check_sizeof_wchar = '''
443 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
449 conf.Message('Checking the size of wchar_t... ')
450 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
452 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
456 conf.Result(str(ret))
460 def checkDeclaration(conf, func, headers):
461 ''' check if a function is declared in given headers '''
467 char *p = (char *) %s;
471 conf.Message('Checking for the declaration of function %s... ' % func)
472 ret = True in [conf.TryLink(check_decl % header, '.c') for header in headers]
477 def createConfigFile(conf, config_file,
478 config_pre = '', config_post = '',
479 headers = [], functions = [], declarations = [], types = [], libs = [],
480 custom_tests = [], extra_items = []):
481 ''' create a configuration file, with options
482 config_file: which file to create
483 config_pre: first part of the config file
484 config_post: last part of the config file
485 headers: header files to check, in the form of a list of
486 ('file', 'HAVE_FILE', 'c'/'c++')
487 functions: functions to check, in the form of a list of
488 ('func', 'HAVE_func', 'include lines'/None)
489 declarations: function declarations to check, in the form of a list of
490 ('func', 'HAVE_DECL_func', header_files)
491 types: types to check, in the form of a list of
492 ('type', 'HAVE_TYPE', 'includelines'/None)
493 libs: libraries to check, in the form of a list of
494 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
495 or any of the libs exists if 'lib' is a list of libs.
496 Optionally, user can provide another key LIB_NAME, that will
497 be set to the detected lib (or None otherwise).
498 custom_tests: extra tests to perform, in the form of a list of
499 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
500 If the last two are ignored, '#define key 1' '/*#undef key */'
502 extra_items: extra configuration lines, in the form of a list of
503 ('config', 'description')
505 The result of each test, as a dictioanry of
506 res['XXX'] = True/False
507 XXX are keys defined in each argument.
509 cont = config_pre + '\n'
511 # add to this string, in appropriate format
512 def configString(lines, desc=''):
514 if lines.strip() != '':
516 text += '/* ' + desc + ' */\n'
517 text += lines + '\n\n'
521 for header in headers:
522 description = "Define to 1 if you have the <%s> header file." % header[0]
523 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
524 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
525 result[header[1]] = 1
526 cont += configString('#define %s 1' % header[1], desc = description)
528 result[header[1]] = 0
529 cont += configString('/* #undef %s */' % header[1], desc = description)
531 for func in functions:
532 description = "Define to 1 if you have the `%s' function." % func[0]
533 if conf.CheckFunc(func[0], header=func[2]):
535 cont += configString('#define %s 1' % func[1], desc = description)
538 cont += configString('/* #undef %s */' % func[1], desc = description)
539 for decl in declarations:
540 description = "Define to 1 if you have the declaration of `%s', and to 0 if you don't." % decl[0]
541 if conf.CheckDeclaration(decl[0], decl[2]):
543 cont += configString('#define %s 1' % decl[1], desc = description)
546 cont += configString('/* #undef %s */' % decl[1], desc = description)
549 description = "Define to 1 if you have the `%s' type." % t[0]
550 if conf.CheckType(t[0], includes=t[2]):
552 cont += configString('#define %s 1' % t[1], desc = description)
555 cont += configString('/* #undef %s */' % t[1], desc = description)
558 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
559 if type(lib[0]) is type(''):
563 # check if any of the lib exists
565 # if user want the name of the lib detected
567 result[lib[2]] = None
569 if conf.CheckLib(ll):
573 cont += configString('#define %s 1' % lib[1], desc = description)
576 if not result[lib[1]]:
577 cont += configString('/* #undef %s */' % lib[1], desc = description)
579 for test in custom_tests:
583 cont += configString('#define %s 1' % test[1], desc = test[2])
585 cont += configString(test[3], desc = test[2])
589 cont += configString('/* #undef %s */' % test[1], desc = test[2])
591 cont += configString(test[4], desc = test[2])
592 # extra items (no key is returned)
593 for item in extra_items:
594 cont += configString(item[0], desc = item[1])
596 cont += '\n' + config_post + '\n'
598 writeToFile(config_file, cont)
602 def installCygwinLDScript(path):
603 ''' Install i386pe.x-no-rdata '''
604 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
605 script = open(ld_script, 'w')
606 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
608 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
609 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
611 OUTPUT_FORMAT(pei-i386)
612 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
613 ENTRY(_mainCRTStartup)
616 .text __image_base__ + __section_alignment__ :
623 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
624 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
625 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
626 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
628 /* ??? Why is .gcc_exc here? */
633 /* The Cygwin32 library uses a section to avoid copying certain data
634 on fork. This used to be named ".data". The linker used
635 to include this between __data_start__ and __data_end__, but that
636 breaks building the cygwin32 dll. Instead, we name the section
637 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
638 .data BLOCK(__section_alignment__) :
647 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
648 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
649 *(.rdata_runtime_pseudo_reloc)
650 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
651 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
653 *(.data_cygwin_nocopy)
655 .rdata BLOCK(__section_alignment__) :
658 .pdata BLOCK(__section_alignment__) :
662 .bss BLOCK(__section_alignment__) :
669 .edata BLOCK(__section_alignment__) :
680 .idata BLOCK(__section_alignment__) :
682 /* This cannot currently be handled with grouped sections.
683 See pe.em:sort_sections. */
686 /* These zeroes mark the end of the import list. */
687 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
693 .CRT BLOCK(__section_alignment__) :
695 ___crt_xc_start__ = . ;
696 *(SORT(.CRT$XC*)) /* C initialization */
697 ___crt_xc_end__ = . ;
698 ___crt_xi_start__ = . ;
699 *(SORT(.CRT$XI*)) /* C++ initialization */
700 ___crt_xi_end__ = . ;
701 ___crt_xl_start__ = . ;
702 *(SORT(.CRT$XL*)) /* TLS callbacks */
703 /* ___crt_xl_end__ is defined in the TLS Directory support code */
704 ___crt_xp_start__ = . ;
705 *(SORT(.CRT$XP*)) /* Pre-termination */
706 ___crt_xp_end__ = . ;
707 ___crt_xt_start__ = . ;
708 *(SORT(.CRT$XT*)) /* Termination */
709 ___crt_xt_end__ = . ;
711 .tls BLOCK(__section_alignment__) :
719 .endjunk BLOCK(__section_alignment__) :
721 /* end is deprecated, don't use it */
726 .rsrc BLOCK(__section_alignment__) :
731 .reloc BLOCK(__section_alignment__) :
735 .stab BLOCK(__section_alignment__) (NOLOAD) :
739 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
743 /* DWARF debug sections.
744 Symbols in the DWARF debugging sections are relative to the beginning
745 of the section. Unlike other targets that fake this by putting the
746 section VMA at 0, the PE format will not allow it. */
747 /* DWARF 1.1 and DWARF 2. */
748 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
752 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
757 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
759 *(.debug_info) *(.gnu.linkonce.wi.*)
761 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
765 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
769 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
773 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
777 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
781 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
785 /* SGI/MIPS DWARF 2 extensions. */
786 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
790 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
794 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
798 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
803 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
813 def installCygwinPostinstallScript(path):
814 ''' Install lyx.sh '''
815 postinstall_script = os.path.join(path, 'lyx.sh')
816 script = open(postinstall_script, 'w')
817 script.write(r'''#!/bin/sh
819 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
820 # if it is not already there.
821 if [ -f /etc/fonts/local.conf ]; then
822 grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
823 if [ $? -ne 0 ]; then
824 sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
825 mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
826 fc-cache /usr/share/lyx/fonts
831 return(postinstall_script)
835 # these will be used under win32
841 # does not matter if it fails on other systems
846 def __init__(self, env, logfile, longarg, info):
847 # save the spawn system
849 self.logfile = logfile
850 # clear the logfile (it may not exist)
852 # this will overwrite existing content.
853 writeToFile(logfile, info, append=False)
855 self.longarg = longarg
856 # get hold of the old spawn? (necessary?)
857 self._spawn = env['SPAWN']
860 def spawn(self, sh, escape, cmd, args, spawnenv):
862 newargs = ' '.join(map(escape, args[1:]))
863 cmdline = cmd + " " + newargs
865 # if log is not empty, write to it
866 if self.logfile != '':
867 # this tend to be slow (?) but ensure correct output
868 # Note that cmdline may be long so I do not escape it
870 # since this is not an essential operation, proceed if things go wrong here.
871 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
873 print "Warning: can not write to log file ", self.logfile
875 # if the command is not too long, use the old
876 if not self.longarg or len(cmdline) < 8000:
877 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
879 sAttrs = win32security.SECURITY_ATTRIBUTES()
880 StartupInfo = win32process.STARTUPINFO()
882 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
883 # check for any special operating system commands
886 win32file.DeleteFile(arg)
889 # otherwise execute the command.
890 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
891 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
892 exit_code = win32process.GetExitCodeProcess(hProcess)
893 win32file.CloseHandle(hProcess);
894 win32file.CloseHandle(hThread);
898 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
899 ''' This function modify env and allow logging of
900 commands to a logfile. If the argument is too long
901 a win32 spawn will be used instead of the system one
904 # create a new spwn object
905 ls = loggedSpawn(env, logfile, longarg, info)
906 # replace the old SPAWN by the new function
907 env['SPAWN'] = ls.spawn