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/*'''
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 env_cat(target, source, env):
235 '''Cat source > target. Avoid pipe to increase portability'''
236 output = open(env.File(target[0]).abspath, 'w')
238 input = open(env.File(src).abspath)
239 output.write(input.read())
244 def createResFromIcon(env, icon_file, rc_file):
245 ''' create a rc file with icon, and return res file (windows only) '''
247 rc_name = env.File(rc_file).abspath
248 dir = os.path.split(rc_name)[0]
249 if not os.path.isdir(dir):
251 rc = open(rc_name, 'w')
252 print >> rc, 'IDI_ICON1 ICON DISCARDABLE "%s"' % \
253 os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'development', 'win32',
254 'packaging', 'icons', icon_file).replace('\\', '\\\\')
256 return env.RES(rc_name)
265 def checkPkgConfig(conf, version):
266 ''' Return false if pkg_config does not exist, or is too old '''
267 conf.Message('Checking for pkg-config...')
268 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
273 def checkPackage(conf, pkg):
274 ''' check if pkg is under the control of conf '''
275 conf.Message('Checking for package %s...' % pkg)
276 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
281 def checkMkdirOneArg(conf):
282 check_mkdir_one_arg_source = """
283 #include <sys/stat.h>
289 conf.Message('Checking for the number of args for mkdir... ')
290 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
291 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
292 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
300 def checkCXXGlobalCstd(conf):
301 ''' Checking the use of std::tolower or tolower '''
302 check_global_cstd_source = '''
310 conf.Message('Checking for the use of global cstd... ')
311 ret = conf.TryLink(check_global_cstd_source, '.c')
316 def checkSelectArgType(conf):
317 ''' Adapted from autoconf '''
318 conf.Message('Checking for arg types for select... ')
319 for arg234 in ['fd_set *', 'int *', 'void *']:
320 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
321 for arg5 in ['struct timeval *', 'const struct timeval *']:
322 check_select_source = '''
323 #if HAVE_SYS_SELECT_H
324 # include <sys/select.h>
326 #if HAVE_SYS_SOCKET_H
327 # include <sys/socket.h>
329 extern int select (%s, %s, %s, %s, %s);
334 ''' % (arg1, arg234, arg234, arg234, arg5)
335 ret = conf.TryLink(check_select_source, '.c')
338 return (arg1, arg234, arg5)
339 conf.Result('no (use default)')
340 return ('int', 'int *', 'struct timeval *')
343 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
344 ''' look for boost libraries
346 lib_paths: try these paths for boost libraries
347 inc_paths: try these paths for boost headers
348 versions: supported boost versions
349 isDebug: if true, use debug libraries
351 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
352 libprefix = conf.env['LIBPREFIX']
353 libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
359 for path in lib_paths:
360 conf.Log("Looking into %s\n" % path)
362 # get all the libs, then filter for the right library
363 files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
364 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
366 conf.Log("Find boost libraries: %s\n" % files)
367 # runtime code includes s,g,y,d,p,n, where we should look for
368 # d,g,y for debug, s,p,n for release
372 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
375 lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
376 if len(lib_files) == 0:
377 # use alternative libraries
379 lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
380 if len(lib_files) > 0:
381 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
382 name = lib_files[0].split(os.sep)[-1][len(libprefix):]
383 lib_names.append(name.split('.')[0])
384 conf.Log("Qualified libraries: %s\n" % lib_names)
386 conf.Log("No qualified library is found.\n")
388 if len(lib_names) == len(libs):
393 if len(lib_names) == 0:
394 conf.Log("No boost library is found\n")
396 conf.Log("Found boost libraries: %s\n" % lib_names)
398 return (None, None, None)
399 # check version number in boost/version.hpp
400 def isValidBoostDir(dir):
401 version_file = os.path.join(dir, 'boost', 'version.hpp')
402 if not os.path.isfile(version_file):
404 version_file_content = open(version_file).read()
405 version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
406 return True in [x in version_file_content for x in version_strings]
407 # check for boost header file
408 for path in inc_paths:
409 conf.Log("Checking for inc path: %s\n" % path)
410 if isValidBoostDir(path):
413 else: # check path/boost_1_xx_x/boost
414 dirs = glob.glob(os.path.join(path, 'boost-*'))
415 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
416 conf.Log("Checing for sub directory: %s\n" % dirs[0])
422 conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
423 return (lib_names, lib_path, inc_path)
426 return (None, None, None)
429 def checkCommand(conf, cmd):
430 ''' check the existence of a command
431 return full path to the command, or none
433 conf.Message('Checking for command %s...' % cmd)
435 conf.Result(res is not None)
440 ''' check the existence of nsis compiler, return the fullpath '''
441 conf.Message('Checking for nsis compiler...')
444 # If we can read the registry, get the NSIS command from it
446 k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
448 val, tok = RegQueryValueEx(k,None)
449 ret = val + os.path.sep + 'makensis.exe'
450 if os.path.isfile(ret):
451 res = '"' + ret + '"'
455 pass # Couldn't find the key, just act like we can't read the registry
456 # Hope it's on the path
458 res = WhereIs('makensis.exe')
459 conf.Result(res is not None)
463 def checkLC_MESSAGES(conf):
464 ''' check the definition of LC_MESSAGES '''
465 check_LC_MESSAGES = '''
472 conf.Message('Checking for LC_MESSAGES in locale.h... ')
473 ret = conf.TryLink(check_LC_MESSAGES, '.c')
478 def checkIconvConst(conf):
479 ''' check the declaration of iconv '''
480 check_iconv_const = '''
482 // this declaration will fail when there already exists a non const char**
483 // version which returns size_t
484 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
489 conf.Message('Checking if the declaration of iconv needs const... ')
490 ret = conf.TryLink(check_iconv_const, '.c')
495 def checkSizeOfWChar(conf):
496 ''' check the size of wchar '''
497 check_sizeof_wchar = '''
498 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
504 conf.Message('Checking the size of wchar_t... ')
505 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
507 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
511 conf.Result(str(ret))
515 def createConfigFile(conf, config_file,
516 config_pre = '', config_post = '',
517 headers = [], functions = [], types = [], libs = [],
518 custom_tests = [], extra_items = []):
519 ''' create a configuration file, with options
520 config_file: which file to create
521 config_pre: first part of the config file
522 config_post: last part of the config file
523 headers: header files to check, in the form of a list of
524 ('file', 'HAVE_FILE', 'c'/'c++')
525 functions: functions to check, in the form of a list of
526 ('func', 'HAVE_func', 'include lines'/None)
527 types: types to check, in the form of a list of
528 ('type', 'HAVE_TYPE', 'includelines'/None)
529 libs: libraries to check, in the form of a list of
530 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
531 or any of the libs exists if 'lib' is a list of libs.
532 Optionally, user can provide another key LIB_NAME, that will
533 be set to the detected lib (or None otherwise).
534 custom_tests: extra tests to perform, in the form of a list of
535 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
536 If the last two are ignored, '#define key 1' '/*#undef key */'
538 extra_items: extra configuration lines, in the form of a list of
539 ('config', 'description')
541 The result of each test, as a dictioanry of
542 res['XXX'] = True/False
543 XXX are keys defined in each argument.
545 cont = config_pre + '\n'
547 # add to this string, in appropriate format
548 def configString(lines, desc=''):
550 if lines.strip() != '':
552 text += '/* ' + desc + ' */\n'
553 text += lines + '\n\n'
557 for header in headers:
558 description = "Define to 1 if you have the <%s> header file." % header[0]
559 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
560 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
561 result[header[1]] = 1
562 cont += configString('#define %s 1' % header[1], desc = description)
564 result[header[1]] = 0
565 cont += configString('/* #undef %s */' % header[1], desc = description)
567 for func in functions:
568 description = "Define to 1 if you have the `%s' function." % func[0]
569 if conf.CheckFunc(func[0], header=func[2]):
571 cont += configString('#define %s 1' % func[1], desc = description)
574 cont += configString('/* #undef %s */' % func[1], desc = description)
577 description = "Define to 1 if you have the `%s' type." % t[0]
578 if conf.CheckType(t[0], includes=t[2]):
580 cont += configString('#define %s 1' % t[1], desc = description)
583 cont += configString('/* #undef %s */' % t[1], desc = description)
586 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
587 if type(lib[0]) is type(''):
591 # check if any of the lib exists
593 # if user want the name of the lib detected
595 result[lib[2]] = None
597 if conf.CheckLib(ll):
601 cont += configString('#define %s 1' % lib[1], desc = description)
604 if not result[lib[1]]:
605 cont += configString('/* #undef %s */' % lib[1], desc = description)
607 for test in custom_tests:
611 cont += configString('#define %s 1' % test[1], desc = test[2])
613 cont += configString(test[3], desc = test[2])
617 cont += configString('/* #undef %s */' % test[1], desc = test[2])
619 cont += configString(test[4], desc = test[2])
620 # extra items (no key is returned)
621 for item in extra_items:
622 cont += configString(item[0], desc = item[1])
624 cont += '\n' + config_post + '\n'
626 writeToFile(config_file, cont)
630 def installCygwinLDScript(path):
631 ''' Install i386pe.x-no-rdata '''
632 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
633 script = open(ld_script, 'w')
634 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
636 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
637 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
639 OUTPUT_FORMAT(pei-i386)
640 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
641 ENTRY(_mainCRTStartup)
644 .text __image_base__ + __section_alignment__ :
651 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
652 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
653 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
654 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
656 /* ??? Why is .gcc_exc here? */
661 /* The Cygwin32 library uses a section to avoid copying certain data
662 on fork. This used to be named ".data". The linker used
663 to include this between __data_start__ and __data_end__, but that
664 breaks building the cygwin32 dll. Instead, we name the section
665 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
666 .data BLOCK(__section_alignment__) :
675 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
676 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
677 *(.rdata_runtime_pseudo_reloc)
678 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
679 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
681 *(.data_cygwin_nocopy)
683 .rdata BLOCK(__section_alignment__) :
686 .pdata BLOCK(__section_alignment__) :
690 .bss BLOCK(__section_alignment__) :
697 .edata BLOCK(__section_alignment__) :
708 .idata BLOCK(__section_alignment__) :
710 /* This cannot currently be handled with grouped sections.
711 See pe.em:sort_sections. */
714 /* These zeroes mark the end of the import list. */
715 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
721 .CRT BLOCK(__section_alignment__) :
723 ___crt_xc_start__ = . ;
724 *(SORT(.CRT$XC*)) /* C initialization */
725 ___crt_xc_end__ = . ;
726 ___crt_xi_start__ = . ;
727 *(SORT(.CRT$XI*)) /* C++ initialization */
728 ___crt_xi_end__ = . ;
729 ___crt_xl_start__ = . ;
730 *(SORT(.CRT$XL*)) /* TLS callbacks */
731 /* ___crt_xl_end__ is defined in the TLS Directory support code */
732 ___crt_xp_start__ = . ;
733 *(SORT(.CRT$XP*)) /* Pre-termination */
734 ___crt_xp_end__ = . ;
735 ___crt_xt_start__ = . ;
736 *(SORT(.CRT$XT*)) /* Termination */
737 ___crt_xt_end__ = . ;
739 .tls BLOCK(__section_alignment__) :
747 .endjunk BLOCK(__section_alignment__) :
749 /* end is deprecated, don't use it */
754 .rsrc BLOCK(__section_alignment__) :
759 .reloc BLOCK(__section_alignment__) :
763 .stab BLOCK(__section_alignment__) (NOLOAD) :
767 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
771 /* DWARF debug sections.
772 Symbols in the DWARF debugging sections are relative to the beginning
773 of the section. Unlike other targets that fake this by putting the
774 section VMA at 0, the PE format will not allow it. */
775 /* DWARF 1.1 and DWARF 2. */
776 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
780 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
785 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
787 *(.debug_info) *(.gnu.linkonce.wi.*)
789 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
793 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
797 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
801 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
805 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
809 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
813 /* SGI/MIPS DWARF 2 extensions. */
814 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
818 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
822 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
826 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
831 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
841 def installCygwinPostinstallScript(path):
842 ''' Install lyx.sh '''
843 postinstall_script = os.path.join(path, 'lyx.sh')
844 script = open(postinstall_script, 'w')
845 script.write(r'''#!/bin/sh
847 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
848 # if it is not already there.
849 if [ -f /etc/fonts/local.conf ]; then
850 grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
851 if [ $? -ne 0 ]; then
852 sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
853 mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
854 fc-cache /usr/share/lyx/fonts
859 return(postinstall_script)
863 # these will be used under win32
869 # does not matter if it fails on other systems
874 def __init__(self, env, logfile, longarg, info):
875 # save the spawn system
877 self.logfile = logfile
878 # clear the logfile (it may not exist)
880 # this will overwrite existing content.
881 writeToFile(logfile, info, append=False)
883 self.longarg = longarg
884 # get hold of the old spawn? (necessary?)
885 self._spawn = env['SPAWN']
888 def spawn(self, sh, escape, cmd, args, spawnenv):
890 newargs = ' '.join(map(escape, args[1:]))
891 cmdline = cmd + " " + newargs
893 # if log is not empty, write to it
894 if self.logfile != '':
895 # this tend to be slow (?) but ensure correct output
896 # Note that cmdline may be long so I do not escape it
898 # since this is not an essential operation, proceed if things go wrong here.
899 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
901 print "Warning: can not write to log file ", self.logfile
903 # if the command is not too long, use the old
904 if not self.longarg or len(cmdline) < 8000:
905 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
907 sAttrs = win32security.SECURITY_ATTRIBUTES()
908 StartupInfo = win32process.STARTUPINFO()
910 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
911 # check for any special operating system commands
914 win32file.DeleteFile(arg)
917 # otherwise execute the command.
918 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
919 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
920 exit_code = win32process.GetExitCodeProcess(hProcess)
921 win32file.CloseHandle(hProcess);
922 win32file.CloseHandle(hThread);
926 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
927 ''' This function modify env and allow logging of
928 commands to a logfile. If the argument is too long
929 a win32 spawn will be used instead of the system one
932 # create a new spwn object
933 ls = loggedSpawn(env, logfile, longarg, info)
934 # replace the old SPAWN by the new function
935 env['SPAWN'] = ls.spawn