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)
189 def env_qtResource(target, source, env):
190 '''Create resource.qrc'''
191 qrc = open(str(target[0]), 'w')
192 print >> qrc, "<!DOCTYPE RCC><RCC version='1.0'><qresource>"
194 rel_file = relativePath(str(file), env.subst('$TOP_SRCDIR/lib'))
195 abs_file = str(file.abspath)
196 print >> qrc, '<file alias="%s">%s</file>' % (rel_file, abs_file)
197 print >> qrc, '</qresource></RCC>'
204 def checkPkgConfig(conf, version):
205 ''' Return false if pkg_config does not exist, or is too old '''
206 conf.Message('Checking for pkg-config...')
207 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
212 def checkPackage(conf, pkg):
213 ''' check if pkg is under the control of conf '''
214 conf.Message('Checking for package %s...' % pkg)
215 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
220 def checkMkdirOneArg(conf):
221 check_mkdir_one_arg_source = """
222 #include <sys/stat.h>
228 conf.Message('Checking for the number of args for mkdir... ')
229 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
230 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
231 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
239 def checkCXXGlobalCstd(conf):
240 ''' Checking the use of std::tolower or tolower '''
241 check_global_cstd_source = '''
249 conf.Message('Checking for the use of global cstd... ')
250 # if can not compile, define CXX_GLOBAL_CSTD
251 ret = conf.TryLink(check_global_cstd_source, '.cpp')
256 def checkSelectArgType(conf):
257 ''' Adapted from autoconf '''
258 conf.Message('Checking for arg types for select... ')
259 for arg234 in ['fd_set *', 'int *', 'void *']:
260 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
261 for arg5 in ['struct timeval *', 'const struct timeval *']:
262 check_select_source = '''
263 #if HAVE_SYS_SELECT_H
264 # include <sys/select.h>
266 #if HAVE_SYS_SOCKET_H
267 # include <sys/socket.h>
269 extern int select (%s, %s, %s, %s, %s);
274 ''' % (arg1, arg234, arg234, arg234, arg5)
275 ret = conf.TryLink(check_select_source, '.c')
278 return (arg1, arg234, arg5)
279 conf.Result('no (use default)')
280 return ('int', 'int *', 'struct timeval *')
283 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
284 ''' look for boost libraries
286 lib_paths: try these paths for boost libraries
287 inc_paths: try these paths for boost headers
288 versions: supported boost versions
289 isDebug: if true, use debug libraries
291 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
292 libprefix = conf.env['LIBPREFIX']
293 libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
299 for path in lib_paths:
300 conf.Log("Looking into %s\n" % path)
302 # get all the libs, then filter for the right library
303 files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
304 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
306 conf.Log("Find boost libraries: %s\n" % files)
307 # runtime code includes s,g,y,d,p,n, where we should look for
308 # d,g,y for debug, s,p,n for release
312 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
315 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
316 if len(lib_files) == 0:
317 # use alternative libraries
319 lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
320 if len(lib_files) > 0:
321 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
322 name = lib_files[0].split(os.sep)[-1][len(libprefix):]
323 lib_names.append(name.split('.')[0])
324 conf.Log("Qualified libraries: %s\n" % lib_names)
326 conf.Log("No qualified library is found.\n")
328 if len(lib_names) == len(libs):
333 if len(lib_names) == 0:
334 conf.Log("No boost library is found\n")
336 conf.Log("Found boost libraries: %s\n" % lib_names)
338 return (None, None, None)
339 # check version number in boost/version.hpp
340 def isValidBoostDir(dir):
341 version_file = os.path.join(dir, 'boost', 'version.hpp')
342 if not os.path.isfile(version_file):
344 version_file_content = open(version_file).read()
345 version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
346 return True in [x in version_file_content for x in version_strings]
347 # check for boost header file
348 for path in inc_paths:
349 conf.Log("Checking for inc path: %s\n" % path)
350 if isValidBoostDir(path):
353 else: # check path/boost_1_xx_x/boost
354 dirs = glob.glob(os.path.join(path, 'boost-*'))
355 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
356 conf.Log("Checing for sub directory: %s\n" % dirs[0])
362 conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
363 return (lib_names, lib_path, inc_path)
366 return (None, None, None)
369 def checkCommand(conf, cmd):
370 ''' check the existence of a command
371 return full path to the command, or none
373 conf.Message('Checking for command %s...' % cmd)
375 conf.Result(res is not None)
380 ''' check the existence of nsis compiler, return the fullpath '''
381 conf.Message('Checking for nsis compiler...')
384 # If we can read the registry, get the NSIS command from it
386 k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
388 val, tok = RegQueryValueEx(k,None)
389 ret = val + os.path.sep + 'makensis.exe'
390 if os.path.isfile(ret):
391 res = '"' + ret + '"'
395 pass # Couldn't find the key, just act like we can't read the registry
396 # Hope it's on the path
398 res = WhereIs('makensis.exe')
399 conf.Result(res is not None)
403 def checkLC_MESSAGES(conf):
404 ''' check the definition of LC_MESSAGES '''
405 check_LC_MESSAGES = '''
412 conf.Message('Checking for LC_MESSAGES in locale.h... ')
413 ret = conf.TryLink(check_LC_MESSAGES, '.c')
418 def checkIconvConst(conf):
419 ''' check the declaration of iconv '''
420 check_iconv_const = '''
422 // this declaration will fail when there already exists a non const char**
423 // version which returns size_t
424 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
429 conf.Message('Checking if the declaration of iconv needs const... ')
430 ret = conf.TryLink(check_iconv_const, '.cpp')
435 def checkSizeOfWChar(conf):
436 ''' check the size of wchar '''
437 check_sizeof_wchar = '''
438 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
444 conf.Message('Checking the size of wchar_t... ')
445 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
447 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
451 conf.Result(str(ret))
455 def checkDeclaration(conf, func, headers):
456 ''' check if a function is declared in given headers '''
462 char *p = (char *) %s;
466 conf.Message('Checking for the declaration of function %s... ' % func)
467 ret = True in [conf.TryLink(check_decl % header, '.c') for header in headers]
472 def createConfigFile(conf, config_file,
473 config_pre = '', config_post = '',
474 headers = [], functions = [], declarations = [], types = [], libs = [],
475 custom_tests = [], extra_items = []):
476 ''' create a configuration file, with options
477 config_file: which file to create
478 config_pre: first part of the config file
479 config_post: last part of the config file
480 headers: header files to check, in the form of a list of
481 ('file', 'HAVE_FILE', 'c'/'c++')
482 functions: functions to check, in the form of a list of
483 ('func', 'HAVE_func', 'include lines'/None)
484 declarations: function declarations to check, in the form of a list of
485 ('func', 'HAVE_DECL_func', header_files)
486 types: types to check, in the form of a list of
487 ('type', 'HAVE_TYPE', 'includelines'/None)
488 libs: libraries to check, in the form of a list of
489 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
490 or any of the libs exists if 'lib' is a list of libs.
491 Optionally, user can provide another key LIB_NAME, that will
492 be set to the detected lib (or None otherwise).
493 custom_tests: extra tests to perform, in the form of a list of
494 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
495 If the last two are ignored, '#define key 1' '/*#undef key */'
497 extra_items: extra configuration lines, in the form of a list of
498 ('config', 'description')
500 The result of each test, as a dictioanry of
501 res['XXX'] = True/False
502 XXX are keys defined in each argument.
504 cont = config_pre + '\n'
506 # add to this string, in appropriate format
507 def configString(lines, desc=''):
509 if lines.strip() != '':
511 text += '/* ' + desc + ' */\n'
512 text += lines + '\n\n'
516 for header in headers:
517 description = "Define to 1 if you have the <%s> header file." % header[0]
518 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
519 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
520 result[header[1]] = 1
521 cont += configString('#define %s 1' % header[1], desc = description)
523 result[header[1]] = 0
524 cont += configString('/* #undef %s */' % header[1], desc = description)
526 for func in functions:
527 description = "Define to 1 if you have the `%s' function." % func[0]
528 if conf.CheckFunc(func[0], header=func[2]):
530 cont += configString('#define %s 1' % func[1], desc = description)
533 cont += configString('/* #undef %s */' % func[1], desc = description)
534 for decl in declarations:
535 description = "Define to 1 if you have the declaration of `%s', and to 0 if you don't." % decl[0]
536 if conf.CheckDeclaration(decl[0], decl[2]):
538 cont += configString('#define %s 1' % decl[1], desc = description)
541 cont += configString('/* #undef %s */' % decl[1], desc = description)
544 description = "Define to 1 if you have the `%s' type." % t[0]
545 if conf.CheckType(t[0], includes=t[2]):
547 cont += configString('#define %s 1' % t[1], desc = description)
550 cont += configString('/* #undef %s */' % t[1], desc = description)
553 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
554 if type(lib[0]) is type(''):
558 # check if any of the lib exists
560 # if user want the name of the lib detected
562 result[lib[2]] = None
564 if conf.CheckLib(ll):
568 cont += configString('#define %s 1' % lib[1], desc = description)
571 if not result[lib[1]]:
572 cont += configString('/* #undef %s */' % lib[1], desc = description)
574 for test in custom_tests:
578 cont += configString('#define %s 1' % test[1], desc = test[2])
580 cont += configString(test[3], desc = test[2])
584 cont += configString('/* #undef %s */' % test[1], desc = test[2])
586 cont += configString(test[4], desc = test[2])
587 # extra items (no key is returned)
588 for item in extra_items:
589 cont += configString(item[0], desc = item[1])
591 cont += '\n' + config_post + '\n'
593 writeToFile(config_file, cont)
597 def installCygwinLDScript(path):
598 ''' Install i386pe.x-no-rdata '''
599 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
600 script = open(ld_script, 'w')
601 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
603 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
604 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
606 OUTPUT_FORMAT(pei-i386)
607 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
608 ENTRY(_mainCRTStartup)
611 .text __image_base__ + __section_alignment__ :
618 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
619 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
620 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
621 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
623 /* ??? Why is .gcc_exc here? */
628 /* The Cygwin32 library uses a section to avoid copying certain data
629 on fork. This used to be named ".data". The linker used
630 to include this between __data_start__ and __data_end__, but that
631 breaks building the cygwin32 dll. Instead, we name the section
632 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
633 .data BLOCK(__section_alignment__) :
642 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
643 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
644 *(.rdata_runtime_pseudo_reloc)
645 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
646 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
648 *(.data_cygwin_nocopy)
650 .rdata BLOCK(__section_alignment__) :
653 .pdata BLOCK(__section_alignment__) :
657 .bss BLOCK(__section_alignment__) :
664 .edata BLOCK(__section_alignment__) :
675 .idata BLOCK(__section_alignment__) :
677 /* This cannot currently be handled with grouped sections.
678 See pe.em:sort_sections. */
681 /* These zeroes mark the end of the import list. */
682 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
688 .CRT BLOCK(__section_alignment__) :
690 ___crt_xc_start__ = . ;
691 *(SORT(.CRT$XC*)) /* C initialization */
692 ___crt_xc_end__ = . ;
693 ___crt_xi_start__ = . ;
694 *(SORT(.CRT$XI*)) /* C++ initialization */
695 ___crt_xi_end__ = . ;
696 ___crt_xl_start__ = . ;
697 *(SORT(.CRT$XL*)) /* TLS callbacks */
698 /* ___crt_xl_end__ is defined in the TLS Directory support code */
699 ___crt_xp_start__ = . ;
700 *(SORT(.CRT$XP*)) /* Pre-termination */
701 ___crt_xp_end__ = . ;
702 ___crt_xt_start__ = . ;
703 *(SORT(.CRT$XT*)) /* Termination */
704 ___crt_xt_end__ = . ;
706 .tls BLOCK(__section_alignment__) :
714 .endjunk BLOCK(__section_alignment__) :
716 /* end is deprecated, don't use it */
721 .rsrc BLOCK(__section_alignment__) :
726 .reloc BLOCK(__section_alignment__) :
730 .stab BLOCK(__section_alignment__) (NOLOAD) :
734 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
738 /* DWARF debug sections.
739 Symbols in the DWARF debugging sections are relative to the beginning
740 of the section. Unlike other targets that fake this by putting the
741 section VMA at 0, the PE format will not allow it. */
742 /* DWARF 1.1 and DWARF 2. */
743 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
747 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
752 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
754 *(.debug_info) *(.gnu.linkonce.wi.*)
756 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
760 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
764 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
768 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
772 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
776 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
780 /* SGI/MIPS DWARF 2 extensions. */
781 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
785 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
789 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
793 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
798 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
808 def installCygwinPostinstallScript(path):
809 ''' Install lyx.sh '''
810 postinstall_script = os.path.join(path, 'lyx.sh')
811 script = open(postinstall_script, 'w')
812 script.write(r'''#!/bin/sh
814 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
815 # if it is not already there.
816 if [ -f /etc/fonts/local.conf ]; then
817 grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
818 if [ $? -ne 0 ]; then
819 sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
820 mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
821 fc-cache /usr/share/lyx/fonts
826 return(postinstall_script)
830 # these will be used under win32
836 # does not matter if it fails on other systems
841 def __init__(self, env, logfile, longarg, info):
842 # save the spawn system
844 self.logfile = logfile
845 # clear the logfile (it may not exist)
847 # this will overwrite existing content.
848 writeToFile(logfile, info, append=False)
850 self.longarg = longarg
851 # get hold of the old spawn? (necessary?)
852 self._spawn = env['SPAWN']
855 def spawn(self, sh, escape, cmd, args, spawnenv):
857 newargs = ' '.join(map(escape, args[1:]))
858 cmdline = cmd + " " + newargs
860 # if log is not empty, write to it
861 if self.logfile != '':
862 # this tend to be slow (?) but ensure correct output
863 # Note that cmdline may be long so I do not escape it
865 # since this is not an essential operation, proceed if things go wrong here.
866 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
868 print "Warning: can not write to log file ", self.logfile
870 # if the command is not too long, use the old
871 if not self.longarg or len(cmdline) < 8000:
872 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
874 sAttrs = win32security.SECURITY_ATTRIBUTES()
875 StartupInfo = win32process.STARTUPINFO()
877 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
878 # check for any special operating system commands
881 win32file.DeleteFile(arg)
884 # otherwise execute the command.
885 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
886 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
887 exit_code = win32process.GetExitCodeProcess(hProcess)
888 win32file.CloseHandle(hProcess);
889 win32file.CloseHandle(hThread);
893 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
894 ''' This function modify env and allow logging of
895 commands to a logfile. If the argument is too long
896 a win32 spawn will be used instead of the system one
899 # create a new spwn object
900 ls = loggedSpawn(env, logfile, longarg, info)
901 # replace the old SPAWN by the new function
902 env['SPAWN'] = ls.spawn