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 "
22 config = open(os.path.join(path, 'configure.ac'))
24 print "Can not open configure.ac. "
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():
31 (version,) = pat.match(line).groups()
32 return version.strip()
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):
44 file = open(filename, 'a')
46 file = open(filename, 'w')
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
55 %key% (not key itself) is an indication of substitution
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")
62 contents = source_file.read()
63 for k, v in env.items():
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)
73 target_file.write(contents + "\n")
75 #st = os.stat(str(source[0]))
76 #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
79 def env_nsis(source, target, env, for_signature):
80 ''' Get nsis command line '''
81 def quoteIfSpaced(str):
83 return '"' + str + '"'
86 ret = env['NSIS'] + " /V1 "
87 if env.has_key('NSISFLAGS'):
88 for flag in env['NSISFLAGS']:
91 if env.has_key('NSISDEFINES'):
92 for d in env['NSISDEFINES']:
94 if env['NSISDEFINES'][d]:
95 ret += '=' + quoteIfSpaced(env['NSISDEFINES'][d])
98 if '-bundle.exe' in str(target[0]):
99 ret += '/DSETUPTYPE_BUNDLE=1 '
101 ret += quoteIfSpaced(str(s))
105 def env_toc(target, source, env):
106 '''Generate target from source files'''
107 # this is very tricky because we need to use installed lyx2lyx with
108 # correct lyx2lyx_version.py
109 sys.path.append(env['LYX2LYX_DEST'])
110 sys.path.append(env.Dir('$TOP_SRCDIR/lib/doc').abspath)
113 doc_toc.build_toc(str(target[0]), [file.abspath for file in source])
116 def relativePath(env, path):
117 '''return relative path from top source dir'''
118 # full pathname of path
119 path1 = os.path.normpath(env.File(path).abspath).split(os.sep)
120 path2 = os.path.normpath(env.Dir('$TOP_SRCDIR').abspath).split(os.sep)
121 if path1[:len(path2)] != path2:
122 print "Path %s is not under top source directory" % path
123 return os.path.join(*path1[len(path2):])
126 def env_language_l10n(target, source, env):
127 '''Generate pot file from lib/language'''
128 input = open(env.File(source[0]).abspath)
129 output = open(env.File(target[0]).abspath, 'w')
130 for lineno, line in enumerate(input.readlines()):
133 items = line.split('"')
136 print 'Warning: this line looks strange:'
139 # afrikaans afrikaans "Afrikaans" false iso8859-15 af_ZA ""
144 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % (relativePath(env, source[0]), lineno+1, items[1])
149 def env_qt4_l10n(target, source, env):
150 '''Generate pot file from src/frontends/qt4/ui/*.ui'''
151 output = open(env.File(target[0]).abspath, 'w')
152 pat = re.compile(r'\s*<string>(.*)</string>')
153 prop = re.compile(r'\s*<property.*name.*=.*shortcut')
155 input = open(env.File(src).abspath)
157 for lineno, line in enumerate(input.readlines()):
158 # looking for a line with <string></string>
162 # skip the line after <property name=shortcut>
166 # get lines that match <string>...</string>
168 (string,) = pat.match(line).groups()
169 string = string.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', r'\"')
170 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
171 (relativePath(env, src), lineno+1, string)
176 def env_layouts_l10n(target, source, env):
177 '''Generate pot file from lib/layouts/*.layout and *.inc'''
178 output = open(env.File(target[0]).abspath, 'w')
179 Style = re.compile(r'^Style\s+(.*)')
180 # include ???LabelString???, but exclude comment lines
181 LabelString = re.compile(r'^[^#]*LabelString\S*\s+(.*)')
182 GuiName = re.compile(r'\s*GuiName\s+(.*)')
183 ListName = re.compile(r'\s*ListName\s+(.*)')
185 input = open(env.File(src).abspath)
186 for lineno, line in enumerate(input.readlines()):
187 # get lines that match <string>...</string>
188 if Style.match(line):
189 (string,) = Style.match(line).groups()
190 string = string.replace('_', ' ')
191 elif LabelString.match(line):
192 (string,) = LabelString.match(line).groups()
193 elif GuiName.match(line):
194 (string,) = GuiName.match(line).groups()
195 elif ListName.match(line):
196 (string,) = ListName.match(line).groups()
199 string = string.replace('\\', '\\\\').replace('"', '')
201 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
202 (relativePath(env, src), lineno+1, string)
207 def env_ui_l10n(target, source, env):
208 '''Generate pot file from lib/ui/*.layout and *.inc'''
209 output = open(env.File(target[0]).abspath, 'w')
210 Submenu = re.compile(r'^[^#]*Submenu\s+"([^"]*)"')
211 Toolbar = re.compile(r'^[^#]*Toolbar\s+"[^"]+"\s+"([^"]*)"')
212 Item = re.compile(r'[^#]*Item\s+"([^"]*)"')
214 input = open(env.File(src).abspath)
215 for lineno, line in enumerate(input.readlines()):
216 # get lines that match <string>...</string>
217 if Submenu.match(line):
218 (string,) = Submenu.match(line).groups()
219 string = string.replace('_', ' ')
220 elif Toolbar.match(line):
221 (string,) = Toolbar.match(line).groups()
222 elif Item.match(line):
223 (string,) = Item.match(line).groups()
226 string = string.replace('\\', '\\\\').replace('"', '')
228 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
229 (relativePath(env, src), lineno+1, string)
234 def createResFromIcon(env, icon_file, rc_file):
235 ''' create a rc file with icon, and return res file (windows only) '''
237 rc_name = env.File(rc_file).abspath
238 dir = os.path.split(rc_name)[0]
239 if not os.path.isdir(dir):
241 rc = open(rc_name, 'w')
242 print >> rc, 'IDI_ICON1 ICON DISCARDABLE "%s"' % \
243 os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'development', 'win32',
244 'packaging', 'icons', icon_file).replace('\\', '\\\\')
246 return env.RES(rc_name)
255 def checkPkgConfig(conf, version):
256 ''' Return false if pkg_config does not exist, or is too old '''
257 conf.Message('Checking for pkg-config...')
258 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
263 def checkPackage(conf, pkg):
264 ''' check if pkg is under the control of conf '''
265 conf.Message('Checking for package %s...' % pkg)
266 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
271 def checkMkdirOneArg(conf):
272 check_mkdir_one_arg_source = """
273 #include <sys/stat.h>
279 conf.Message('Checking for the number of args for mkdir... ')
280 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
281 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
282 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
290 def checkCXXGlobalCstd(conf):
291 ''' Checking the use of std::tolower or tolower '''
292 check_global_cstd_source = '''
300 conf.Message('Checking for the use of global cstd... ')
301 ret = conf.TryLink(check_global_cstd_source, '.c')
306 def checkSelectArgType(conf):
307 ''' Adapted from autoconf '''
308 conf.Message('Checking for arg types for select... ')
309 for arg234 in ['fd_set *', 'int *', 'void *']:
310 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
311 for arg5 in ['struct timeval *', 'const struct timeval *']:
312 check_select_source = '''
313 #if HAVE_SYS_SELECT_H
314 # include <sys/select.h>
316 #if HAVE_SYS_SOCKET_H
317 # include <sys/socket.h>
319 extern int select (%s, %s, %s, %s, %s);
324 ''' % (arg1, arg234, arg234, arg234, arg5)
325 ret = conf.TryLink(check_select_source, '.c')
328 return (arg1, arg234, arg5)
329 conf.Result('no (use default)')
330 return ('int', 'int *', 'struct timeval *')
333 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
334 ''' look for boost libraries
336 lib_paths: try these paths for boost libraries
337 inc_paths: try these paths for boost headers
338 versions: supported boost versions
339 isDebug: if true, use debug libraries
341 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
342 libprefix = conf.env['LIBPREFIX']
343 libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
349 for path in lib_paths:
350 conf.Log("Looking into %s\n" % path)
352 # get all the libs, then filter for the right library
353 files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
354 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
356 conf.Log("Find boost libraries: %s\n" % files)
357 # runtime code includes s,g,y,d,p,n, where we should look for
358 # d,g,y for debug, s,p,n for release
362 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
365 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
366 if len(lib_files) == 0:
367 # use alternative libraries
369 lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
370 if len(lib_files) > 0:
371 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
372 name = lib_files[0].split(os.sep)[-1][len(libprefix):]
373 lib_names.append(name.split('.')[0])
374 conf.Log("Qualified libraries: %s\n" % lib_names)
376 conf.Log("No qualified library is found.\n")
378 if len(lib_names) == len(libs):
383 if len(lib_names) == 0:
384 conf.Log("No boost library is found\n")
386 conf.Log("Found boost libraries: %s\n" % lib_names)
388 return (None, None, None)
389 # check version number in boost/version.hpp
390 def isValidBoostDir(dir):
391 version_file = os.path.join(dir, 'boost', 'version.hpp')
392 if not os.path.isfile(version_file):
394 version_file_content = open(version_file).read()
395 version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
396 return True in [x in version_file_content for x in version_strings]
397 # check for boost header file
398 for path in inc_paths:
399 conf.Log("Checking for inc path: %s\n" % path)
400 if isValidBoostDir(path):
403 else: # check path/boost_1_xx_x/boost
404 dirs = glob.glob(os.path.join(path, 'boost-*'))
405 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
406 conf.Log("Checing for sub directory: %s\n" % dirs[0])
412 conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
413 return (lib_names, lib_path, inc_path)
416 return (None, None, None)
419 def checkCommand(conf, cmd):
420 ''' check the existence of a command
421 return full path to the command, or none
423 conf.Message('Checking for command %s...' % cmd)
425 conf.Result(res is not None)
430 ''' check the existence of nsis compiler, return the fullpath '''
431 conf.Message('Checking for nsis compiler...')
434 # If we can read the registry, get the NSIS command from it
436 k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
438 val, tok = RegQueryValueEx(k,None)
439 ret = val + os.path.sep + 'makensis.exe'
440 if os.path.isfile(ret):
441 res = '"' + ret + '"'
445 pass # Couldn't find the key, just act like we can't read the registry
446 # Hope it's on the path
448 res = WhereIs('makensis.exe')
449 conf.Result(res is not None)
453 def checkLC_MESSAGES(conf):
454 ''' check the definition of LC_MESSAGES '''
455 check_LC_MESSAGES = '''
462 conf.Message('Checking for LC_MESSAGES in locale.h... ')
463 ret = conf.TryLink(check_LC_MESSAGES, '.c')
468 def checkIconvConst(conf):
469 ''' check the declaration of iconv '''
470 check_iconv_const = '''
472 // this declaration will fail when there already exists a non const char**
473 // version which returns size_t
474 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
479 conf.Message('Checking if the declaration of iconv needs const... ')
480 ret = conf.TryLink(check_iconv_const, '.c')
485 def checkSizeOfWChar(conf):
486 ''' check the size of wchar '''
487 check_sizeof_wchar = '''
488 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
494 conf.Message('Checking the size of wchar_t... ')
495 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
497 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
501 conf.Result(str(ret))
505 def createConfigFile(conf, config_file,
506 config_pre = '', config_post = '',
507 headers = [], functions = [], types = [], libs = [],
508 custom_tests = [], extra_items = []):
509 ''' create a configuration file, with options
510 config_file: which file to create
511 config_pre: first part of the config file
512 config_post: last part of the config file
513 headers: header files to check, in the form of a list of
514 ('file', 'HAVE_FILE', 'c'/'c++')
515 functions: functions to check, in the form of a list of
516 ('func', 'HAVE_func', 'include lines'/None)
517 types: types to check, in the form of a list of
518 ('type', 'HAVE_TYPE', 'includelines'/None)
519 libs: libraries to check, in the form of a list of
520 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
521 or any of the libs exists if 'lib' is a list of libs.
522 Optionally, user can provide another key LIB_NAME, that will
523 be set to the detected lib (or None otherwise).
524 custom_tests: extra tests to perform, in the form of a list of
525 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
526 If the last two are ignored, '#define key 1' '/*#undef key */'
528 extra_items: extra configuration lines, in the form of a list of
529 ('config', 'description')
531 The result of each test, as a dictioanry of
532 res['XXX'] = True/False
533 XXX are keys defined in each argument.
535 cont = config_pre + '\n'
537 # add to this string, in appropriate format
538 def configString(lines, desc=''):
540 if lines.strip() != '':
542 text += '/* ' + desc + ' */\n'
543 text += lines + '\n\n'
547 for header in headers:
548 description = "Define to 1 if you have the <%s> header file." % header[0]
549 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
550 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
551 result[header[1]] = 1
552 cont += configString('#define %s 1' % header[1], desc = description)
554 result[header[1]] = 0
555 cont += configString('/* #undef %s */' % header[1], desc = description)
557 for func in functions:
558 description = "Define to 1 if you have the `%s' function." % func[0]
559 if conf.CheckFunc(func[0], header=func[2]):
561 cont += configString('#define %s 1' % func[1], desc = description)
564 cont += configString('/* #undef %s */' % func[1], desc = description)
567 description = "Define to 1 if you have the `%s' type." % t[0]
568 if conf.CheckType(t[0], includes=t[2]):
570 cont += configString('#define %s 1' % t[1], desc = description)
573 cont += configString('/* #undef %s */' % t[1], desc = description)
576 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
577 if type(lib[0]) is type(''):
581 # check if any of the lib exists
583 # if user want the name of the lib detected
585 result[lib[2]] = None
587 if conf.CheckLib(ll):
591 cont += configString('#define %s 1' % lib[1], desc = description)
594 if not result[lib[1]]:
595 cont += configString('/* #undef %s */' % lib[1], desc = description)
597 for test in custom_tests:
601 cont += configString('#define %s 1' % test[1], desc = test[2])
603 cont += configString(test[3], desc = test[2])
607 cont += configString('/* #undef %s */' % test[1], desc = test[2])
609 cont += configString(test[4], desc = test[2])
610 # extra items (no key is returned)
611 for item in extra_items:
612 cont += configString(item[0], desc = item[1])
614 cont += '\n' + config_post + '\n'
616 writeToFile(config_file, cont)
620 def installCygwinLDScript(path):
621 ''' Install i386pe.x-no-rdata '''
622 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
623 script = open(ld_script, 'w')
624 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
626 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
627 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
629 OUTPUT_FORMAT(pei-i386)
630 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
631 ENTRY(_mainCRTStartup)
634 .text __image_base__ + __section_alignment__ :
641 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
642 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
643 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
644 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
646 /* ??? Why is .gcc_exc here? */
651 /* The Cygwin32 library uses a section to avoid copying certain data
652 on fork. This used to be named ".data". The linker used
653 to include this between __data_start__ and __data_end__, but that
654 breaks building the cygwin32 dll. Instead, we name the section
655 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
656 .data BLOCK(__section_alignment__) :
665 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
666 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
667 *(.rdata_runtime_pseudo_reloc)
668 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
669 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
671 *(.data_cygwin_nocopy)
673 .rdata BLOCK(__section_alignment__) :
676 .pdata BLOCK(__section_alignment__) :
680 .bss BLOCK(__section_alignment__) :
687 .edata BLOCK(__section_alignment__) :
698 .idata BLOCK(__section_alignment__) :
700 /* This cannot currently be handled with grouped sections.
701 See pe.em:sort_sections. */
704 /* These zeroes mark the end of the import list. */
705 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
711 .CRT BLOCK(__section_alignment__) :
713 ___crt_xc_start__ = . ;
714 *(SORT(.CRT$XC*)) /* C initialization */
715 ___crt_xc_end__ = . ;
716 ___crt_xi_start__ = . ;
717 *(SORT(.CRT$XI*)) /* C++ initialization */
718 ___crt_xi_end__ = . ;
719 ___crt_xl_start__ = . ;
720 *(SORT(.CRT$XL*)) /* TLS callbacks */
721 /* ___crt_xl_end__ is defined in the TLS Directory support code */
722 ___crt_xp_start__ = . ;
723 *(SORT(.CRT$XP*)) /* Pre-termination */
724 ___crt_xp_end__ = . ;
725 ___crt_xt_start__ = . ;
726 *(SORT(.CRT$XT*)) /* Termination */
727 ___crt_xt_end__ = . ;
729 .tls BLOCK(__section_alignment__) :
737 .endjunk BLOCK(__section_alignment__) :
739 /* end is deprecated, don't use it */
744 .rsrc BLOCK(__section_alignment__) :
749 .reloc BLOCK(__section_alignment__) :
753 .stab BLOCK(__section_alignment__) (NOLOAD) :
757 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
761 /* DWARF debug sections.
762 Symbols in the DWARF debugging sections are relative to the beginning
763 of the section. Unlike other targets that fake this by putting the
764 section VMA at 0, the PE format will not allow it. */
765 /* DWARF 1.1 and DWARF 2. */
766 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
770 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
775 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
777 *(.debug_info) *(.gnu.linkonce.wi.*)
779 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
783 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
787 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
791 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
795 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
799 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
803 /* SGI/MIPS DWARF 2 extensions. */
804 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
808 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
812 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
816 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
821 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
831 def installCygwinPostinstallScript(path):
832 ''' Install lyx.sh '''
833 postinstall_script = os.path.join(path, 'lyx.sh')
834 script = open(postinstall_script, 'w')
835 script.write(r'''#!/bin/sh
837 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
838 # if it is not already there.
839 if [ -f /etc/fonts/local.conf ]; then
840 grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
841 if [ $? -ne 0 ]; then
842 sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
843 mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
844 fc-cache /usr/share/lyx/fonts
849 return(postinstall_script)
853 # these will be used under win32
859 # does not matter if it fails on other systems
864 def __init__(self, env, logfile, longarg, info):
865 # save the spawn system
867 self.logfile = logfile
868 # clear the logfile (it may not exist)
870 # this will overwrite existing content.
871 writeToFile(logfile, info, append=False)
873 self.longarg = longarg
874 # get hold of the old spawn? (necessary?)
875 self._spawn = env['SPAWN']
878 def spawn(self, sh, escape, cmd, args, spawnenv):
880 newargs = ' '.join(map(escape, args[1:]))
881 cmdline = cmd + " " + newargs
883 # if log is not empty, write to it
884 if self.logfile != '':
885 # this tend to be slow (?) but ensure correct output
886 # Note that cmdline may be long so I do not escape it
888 # since this is not an essential operation, proceed if things go wrong here.
889 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
891 print "Warning: can not write to log file ", self.logfile
893 # if the command is not too long, use the old
894 if not self.longarg or len(cmdline) < 8000:
895 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
897 sAttrs = win32security.SECURITY_ATTRIBUTES()
898 StartupInfo = win32process.STARTUPINFO()
900 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
901 # check for any special operating system commands
904 win32file.DeleteFile(arg)
907 # otherwise execute the command.
908 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
909 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
910 exit_code = win32process.GetExitCodeProcess(hProcess)
911 win32file.CloseHandle(hProcess);
912 win32file.CloseHandle(hThread);
916 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
917 ''' This function modify env and allow logging of
918 commands to a logfile. If the argument is too long
919 a win32 spawn will be used instead of the system one
922 # create a new spwn object
923 ls = loggedSpawn(env, logfile, longarg, info)
924 # replace the old SPAWN by the new function
925 env['SPAWN'] = ls.spawn