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 WhereIs
19 def writeToFile(filename, lines, append = False):
20 " utility function: write or append lines to filename "
21 # create directory if needed
22 dir = os.path.split(filename)[0]
23 if dir != '' and not os.path.isdir(dir):
26 file = open(filename, 'a')
28 file = open(filename, 'w')
33 def env_subst(target, source, env):
34 ''' subst variables in source by those in env, and output to target
35 source and target are scons File() objects
37 %key% (not key itself) is an indication of substitution
39 assert len(target) == 1
40 assert len(source) == 1
41 target_file = file(str(target[0]), "w")
42 source_file = file(str(source[0]), "r")
44 contents = source_file.read()
45 for k, v in env.items():
47 val = env.subst('$'+k)
48 # temporary fix for the \Resource backslash problem
49 val = val.replace('\\', '/')
50 # multi-line replacement
51 val = val.replace('\n',r'\\n\\\n')
52 contents = re.sub('@'+k+'@', val, contents)
53 contents = re.sub('%'+k+'%', val, contents)
56 target_file.write(contents + "\n")
58 #st = os.stat(str(source[0]))
59 #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
65 def checkPkgConfig(conf, version):
66 ''' Return false if pkg_config does not exist, or is too old '''
67 conf.Message('Checking for pkg-config...')
68 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
73 def checkPackage(conf, pkg):
74 ''' check if pkg is under the control of conf '''
75 conf.Message('Checking for package %s...' % pkg)
76 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
81 def checkMkdirOneArg(conf):
82 check_mkdir_one_arg_source = """
89 conf.Message('Checking for the number of args for mkdir... ')
90 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
91 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
92 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
100 def checkCXXGlobalCstd(conf):
101 ''' Check the use of std::tolower or tolower '''
102 check_global_cstd_source = '''
110 conf.Message('Check for the use of global cstd... ')
111 ret = conf.TryLink(check_global_cstd_source, '.c')
116 def checkSelectArgType(conf):
117 ''' Adapted from autoconf '''
118 conf.Message('Checking for arg types for select... ')
119 for arg234 in ['fd_set *', 'int *', 'void *']:
120 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
121 for arg5 in ['struct timeval *', 'const struct timeval *']:
122 check_select_source = '''
123 #if HAVE_SYS_SELECT_H
124 # include <sys/select.h>
126 #if HAVE_SYS_SOCKET_H
127 # include <sys/socket.h>
129 extern int select (%s, %s, %s, %s, %s);
134 ''' % (arg1, arg234, arg234, arg234, arg5)
135 ret = conf.TryLink(check_select_source, '.c')
138 return (arg1, arg234, arg5)
139 conf.Result('no (use default)')
140 return ('int', 'int *', 'struct timeval *')
143 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, version, isDebug):
144 ''' look for boost libraries
146 lib_paths: try these paths for boost libraries
147 inc_paths: try these paths for boost headers
148 version: required boost version
149 isDebug: if true, use debug libraries
151 conf.Message('Checking for boost library %s... ' % ', '.join(libs))
157 for path in lib_paths:
158 # direct form: e.g. libboost_iostreams.a
160 if False not in [os.path.isfile(os.path.join(path, 'libboost_%s.a' % lib)) for lib in libs]:
167 # get all the libs, then filter for the right library
168 files = glob.glob(os.path.join(path, 'libboost_%s-*.a' % lib))
169 # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
171 # runtime code includes s,g,y,d,p,n, where we should look for
172 # d,g,y for debug, s,p,n for release
174 lib_files = filter(lambda x: re.search('libboost_%s-\w+-mt-[^spn]+-%s.a' % (lib, version), x), files)
176 lib_files = filter(lambda x: re.search('libboost_%s-\w+-mt-([^dgy]+-)*%s.a' % (lib, version), x), files)
177 if len(lib_files) == 0:
178 print 'Warning: Can not find an appropriate boost library in %s.' % path
179 lib_files = filter(lambda x: re.search('libboost_%s-[\w-]+%s.a' % (lib, version), x), files)
180 if len(lib_files) > 0:
181 print 'Use library %s' % lib_files[0]
182 if len(lib_files) > 0:
183 # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
184 lib_names.append(lib_files[0].split(os.sep)[-1][3:-2])
185 if len(lib_names) == len(libs):
191 return (None, None, None)
192 # check version number in boost/version.hpp
193 def isValidBoostDir(dir):
194 file = os.path.join(dir, 'boost', 'version.hpp')
195 version_string = '#define BOOST_LIB_VERSION "%s"' % version
196 return os.path.isfile(file) and version_string in open(file).read()
197 # check for boost header file
198 for path in inc_paths:
199 if isValidBoostDir(path):
202 else: # check path/boost_1_xx_x/boost
203 dirs = glob.glob(os.path.join(path, 'boost-*'))
204 if len(dirs) > 0 and isValidBoostDir(dirs[0]):
210 return (lib_names, lib_path, inc_path)
213 return (None, None, None)
216 def checkCommand(conf, cmd):
217 ''' check the existence of a command
218 return full path to the command, or none
220 conf.Message('Checking for command %s...' % cmd)
222 conf.Result(res is not None)
226 def checkLC_MESSAGES(conf):
227 ''' check the definition of LC_MESSAGES '''
228 check_LC_MESSAGES = '''
235 conf.Message('Check for LC_MESSAGES in locale.h... ')
236 ret = conf.TryLink(check_LC_MESSAGES, '.c')
241 def checkIconvConst(conf):
242 ''' check the declaration of iconv '''
243 check_iconv_const = '''
245 // this declaration will fail when there already exists a non const char**
246 // version which returns size_t
247 double iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
252 conf.Message('Check if the declaration of iconv needs const... ')
253 ret = conf.TryLink(check_iconv_const, '.c')
258 def checkSizeOfWChar(conf):
259 ''' check the size of wchar '''
260 check_sizeof_wchar = '''
261 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
267 conf.Message('Check the size of wchar_t... ')
268 if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
270 elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
274 conf.Result(str(ret))
278 def createConfigFile(conf, config_file,
279 config_pre = '', config_post = '',
280 headers = [], functions = [], types = [], libs = [],
281 custom_tests = [], extra_items = []):
282 ''' create a configuration file, with options
283 config_file: which file to create
284 config_pre: first part of the config file
285 config_post: last part of the config file
286 headers: header files to check, in the form of a list of
287 ('file', 'HAVE_FILE', 'c'/'c++')
288 functions: functions to check, in the form of a list of
289 ('func', 'HAVE_func', 'include lines'/None)
290 types: types to check, in the form of a list of
291 ('type', 'HAVE_TYPE', 'includelines'/None)
292 libs: libraries to check, in the form of a list of
293 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
294 or any of the libs exists if 'lib' is a list of libs.
295 Optionally, user can provide another key LIB_NAME, that will
296 be set to the detected lib (or None otherwise).
297 custom_tests: extra tests to perform, in the form of a list of
298 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
299 If the last two are ignored, '#define key 1' '/*#undef key */'
301 extra_items: extra configuration lines, in the form of a list of
302 ('config', 'description')
304 The result of each test, as a dictioanry of
305 res['XXX'] = True/False
306 XXX are keys defined in each argument.
308 cont = config_pre + '\n'
310 # add to this string, in appropriate format
311 def configString(lines, desc=''):
313 if lines.strip() != '':
315 text += '/* ' + desc + ' */\n'
316 text += lines + '\n\n'
320 for header in headers:
321 description = "Define to 1 if you have the <%s> header file." % header[0]
322 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
323 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
324 result[header[1]] = True
325 cont += configString('#define %s 1' % header[1], desc = description)
327 result[header[1]] = False
328 cont += configString('/* #undef %s */' % header[1], desc = description)
330 for func in functions:
331 description = "Define to 1 if you have the `%s' function." % func[0]
332 if conf.CheckFunc(func[0], header=func[2]):
333 result[func[1]] = True
334 cont += configString('#define %s 1' % func[1], desc = description)
336 result[func[1]] = False
337 cont += configString('/* #undef %s */' % func[1], desc = description)
340 description = "Define to 1 if you have the `%s' type." % t[0]
341 if conf.CheckType(t[0], includes=t[2]):
343 cont += configString('#define %s 1' % t[1], desc = description)
346 cont += configString('/* #undef %s */' % t[1], desc = description)
349 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
350 if type(lib[0]) is type(''):
354 # check if any of the lib exists
355 result[lib[1]] = False
356 # if user want the name of the lib detected
358 result[lib[2]] = None
360 if conf.CheckLib(ll):
361 result[lib[1]] = True
364 cont += configString('#define %s 1' % lib[1], desc = description)
367 if not result[lib[1]]:
368 cont += configString('/* #undef %s */' % lib[1], desc = description)
370 for test in custom_tests:
372 result[test[1]] = True
374 cont += configString('#define %s 1' % test[1], desc = test[2])
376 cont += configString(test[3], desc = test[2])
378 result[test[1]] = False
380 cont += configString('/* #undef %s */' % test[1], desc = test[2])
382 cont += configString(test[4], desc = test[2])
383 # extra items (no key is returned)
384 for item in extra_items:
385 cont += configString(item[0], desc = item[1])
387 cont += '\n' + config_post + '\n'
389 writeToFile(config_file, cont)
393 def installCygwinLDScript(path):
394 ''' Install i386pe.x-no-rdata '''
395 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
396 script = open(ld_script, 'w')
397 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
399 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
400 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
402 OUTPUT_FORMAT(pei-i386)
403 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
404 ENTRY(_mainCRTStartup)
407 .text __image_base__ + __section_alignment__ :
414 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
415 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
416 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
417 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
419 /* ??? Why is .gcc_exc here? */
424 /* The Cygwin32 library uses a section to avoid copying certain data
425 on fork. This used to be named ".data". The linker used
426 to include this between __data_start__ and __data_end__, but that
427 breaks building the cygwin32 dll. Instead, we name the section
428 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
429 .data BLOCK(__section_alignment__) :
438 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
439 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
440 *(.rdata_runtime_pseudo_reloc)
441 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
442 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
444 *(.data_cygwin_nocopy)
446 .rdata BLOCK(__section_alignment__) :
449 .pdata BLOCK(__section_alignment__) :
453 .bss BLOCK(__section_alignment__) :
460 .edata BLOCK(__section_alignment__) :
471 .idata BLOCK(__section_alignment__) :
473 /* This cannot currently be handled with grouped sections.
474 See pe.em:sort_sections. */
477 /* These zeroes mark the end of the import list. */
478 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
484 .CRT BLOCK(__section_alignment__) :
486 ___crt_xc_start__ = . ;
487 *(SORT(.CRT$XC*)) /* C initialization */
488 ___crt_xc_end__ = . ;
489 ___crt_xi_start__ = . ;
490 *(SORT(.CRT$XI*)) /* C++ initialization */
491 ___crt_xi_end__ = . ;
492 ___crt_xl_start__ = . ;
493 *(SORT(.CRT$XL*)) /* TLS callbacks */
494 /* ___crt_xl_end__ is defined in the TLS Directory support code */
495 ___crt_xp_start__ = . ;
496 *(SORT(.CRT$XP*)) /* Pre-termination */
497 ___crt_xp_end__ = . ;
498 ___crt_xt_start__ = . ;
499 *(SORT(.CRT$XT*)) /* Termination */
500 ___crt_xt_end__ = . ;
502 .tls BLOCK(__section_alignment__) :
510 .endjunk BLOCK(__section_alignment__) :
512 /* end is deprecated, don't use it */
517 .rsrc BLOCK(__section_alignment__) :
522 .reloc BLOCK(__section_alignment__) :
526 .stab BLOCK(__section_alignment__) (NOLOAD) :
530 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
534 /* DWARF debug sections.
535 Symbols in the DWARF debugging sections are relative to the beginning
536 of the section. Unlike other targets that fake this by putting the
537 section VMA at 0, the PE format will not allow it. */
538 /* DWARF 1.1 and DWARF 2. */
539 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
543 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
548 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
550 *(.debug_info) *(.gnu.linkonce.wi.*)
552 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
556 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
560 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
564 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
568 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
572 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
576 /* SGI/MIPS DWARF 2 extensions. */
577 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
581 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
585 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
589 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
594 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
605 # these will be used under win32
611 # does not matter if it fails on other systems
616 def __init__(self, env, logfile, longarg, info):
617 # save the spawn system
619 self.logfile = logfile
620 # clear the logfile (it may not exist)
622 # this will overwrite existing content.
623 writeToFile(logfile, info, append=False)
625 self.longarg = longarg
626 # get hold of the old spawn? (necessary?)
627 self._spawn = env['SPAWN']
630 def spawn(self, sh, escape, cmd, args, spawnenv):
632 newargs = ' '.join(map(escape, args[1:]))
633 cmdline = cmd + " " + newargs
635 # if log is not empty, write to it
636 if self.logfile != '':
637 # this tend to be slow (?) but ensure correct output
638 # Note that cmdline may be long so I do not escape it
640 # since this is not an essential operation, proceed if things go wrong here.
641 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
643 print "Warning: can not write to log file ", self.logfile
645 # if the command is not too long, use the old
646 if not self.longarg or len(cmdline) < 8000:
647 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
649 sAttrs = win32security.SECURITY_ATTRIBUTES()
650 StartupInfo = win32process.STARTUPINFO()
652 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
653 # check for any special operating system commands
656 win32file.DeleteFile(arg)
659 # otherwise execute the command.
660 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
661 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
662 exit_code = win32process.GetExitCodeProcess(hProcess)
663 win32file.CloseHandle(hProcess);
664 win32file.CloseHandle(hThread);
668 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
669 ''' This function modify env and allow logging of
670 commands to a logfile. If the argument is too long
671 a win32 spawn will be used instead of the system one
674 # create a new spwn object
675 ls = loggedSpawn(env, logfile, longarg, info)
676 # replace the old SPAWN by the new function
677 env['SPAWN'] = ls.spawn