1 # vi:filetype=python:expandtab:tabstop=2:shiftwidth=2
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 globSource(dir, pattern, build_dir = None, exclude = [], include = []):
66 ''' glob files, in dir and use build_dir as returned path name '''
67 # exclude 'exclude+include' to avoid duplicate items in files
68 files = include + filter(lambda x: x not in exclude + include, glob.glob1(dir, pattern))
72 return ['%s/%s' % (build_dir, x) for x in files]
78 def checkPkgConfig(conf, version):
79 ''' Return false if pkg_config does not exist, or is too old '''
80 conf.Message('Checking for pkg-config...')
81 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
86 def checkPackage(conf, pkg):
87 ''' check if pkg is under the control of conf '''
88 conf.Message('Checking for package %s...' % pkg)
89 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
94 def checkMkdirOneArg(conf):
95 check_mkdir_one_arg_source = """
102 conf.Message('Checking for the number of args for mkdir... ')
103 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
104 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
105 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
113 def checkCXXGlobalCstd(conf):
114 ''' Check the use of std::tolower or tolower '''
115 check_global_cstd_source = '''
123 conf.Message('Check for the use of global cstd... ')
124 ret = conf.TryLink(check_global_cstd_source, '.c')
129 def checkSelectArgType(conf):
130 ''' Adapted from autoconf '''
131 conf.Message('Checking for arg types for select... ')
132 for arg234 in ['fd_set *', 'int *', 'void *']:
133 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
134 for arg5 in ['struct timeval *', 'const struct timeval *']:
135 check_select_source = '''
136 #if HAVE_SYS_SELECT_H
137 # include <sys/select.h>
139 #if HAVE_SYS_SOCKET_H
140 # include <sys/socket.h>
142 extern int select (%s, %s, %s, %s, %s);
147 ''' % (arg1, arg234, arg234, arg234, arg5)
148 ret = conf.TryLink(check_select_source, '.c')
151 return (arg1, arg234, arg5)
152 conf.Result('no (use default)')
153 return ('int', 'int *', 'struct timeval *')
156 def checkBoostLibraries(conf, lib, pathes):
157 ''' look for boost libraries '''
158 conf.Message('Checking for boost library %s... ' % lib)
160 # direct form: e.g. libboost_iostreams.a
161 if os.path.isfile(os.path.join(path, 'lib%s.a' % lib)):
164 # check things like libboost_iostreams-gcc.a
165 files = glob.glob(os.path.join(path, 'lib%s-*.a' % lib))
166 # if there are more than one, choose the first one
167 # FIXME: choose the best one.
169 # get xxx-gcc from /usr/local/lib/libboost_xxx-gcc.a
171 return (path, files[0].split(os.sep)[-1][3:-2])
176 def checkCommand(conf, cmd):
177 ''' check the existence of a command
178 return full path to the command, or none
180 conf.Message('Checking for command %s...' % cmd)
182 conf.Result(res is not None)
186 def checkLC_MESSAGES(conf):
187 ''' check the definition of LC_MESSAGES '''
188 check_LC_MESSAGES = '''
195 conf.Message('Check for LC_MESSAGES in locale.h... ')
196 ret = conf.TryLink(check_LC_MESSAGES, '.c')
201 def checkIconvConst(conf):
202 ''' check the declaration of iconv '''
203 check_iconv_const = '''
210 #if defined(__STDC__) || defined(__cplusplus)
211 #ifndef LIBICONV_DLL_EXPORTED
212 size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
223 conf.Message('Check if the declaration of iconv needs const... ')
224 ret = conf.TryLink(check_iconv_const, '.c')
229 def createConfigFile(conf, config_file,
230 config_pre = '', config_post = '',
231 headers = [], functions = [], types = [], libs = [],
232 custom_tests = [], extra_items = []):
233 ''' create a configuration file, with options
234 config_file: which file to create
235 config_pre: first part of the config file
236 config_post: last part of the config file
237 headers: header files to check, in the form of a list of
238 ('file', 'HAVE_FILE', 'c'/'c++')
239 functions: functions to check, in the form of a list of
240 ('func', 'HAVE_func', 'include lines'/None)
241 types: types to check, in the form of a list of
242 ('type', 'HAVE_TYPE', 'includelines'/None)
243 libs: libraries to check, in the form of a list of
244 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
245 or any of the libs exists if 'lib' is a list of libs.
246 Optionally, user can provide another key LIB_NAME, that will
247 be set to the detected lib (or None otherwise).
248 custom_tests: extra tests to perform, in the form of a list of
249 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
250 If the last two are ignored, '#define key 1' '/*#undef key */'
252 extra_items: extra configuration lines, in the form of a list of
253 ('config', 'description')
255 The result of each test, as a dictioanry of
256 res['XXX'] = True/False
257 XXX are keys defined in each argument.
259 cont = config_pre + '\n'
261 # add to this string, in appropriate format
262 def configString(lines, desc=''):
264 if lines.strip() != '':
266 text += '/* ' + desc + ' */\n'
267 text += lines + '\n\n'
271 for header in headers:
272 description = "Define to 1 if you have the <%s> header file." % header[0]
273 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
274 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
275 result[header[1]] = True
276 cont += configString('#define %s 1' % header[1], desc = description)
278 result[header[1]] = False
279 cont += configString('/* #undef %s */' % header[1], desc = description)
281 for func in functions:
282 description = "Define to 1 if you have the `%s' function." % func[0]
283 if conf.CheckFunc(func[0], header=func[2]):
284 result[func[1]] = True
285 cont += configString('#define %s 1' % func[1], desc = description)
287 result[func[1]] = False
288 cont += configString('/* #undef %s */' % func[1], desc = description)
291 description = "Define to 1 if you have the `%s' type." % t[0]
292 if conf.CheckType(t[0], includes=t[2]):
294 cont += configString('#define %s 1' % t[1], desc = description)
297 cont += configString('/* #undef %s */' % t[1], desc = description)
300 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
301 if type(lib[0]) is type(''):
305 # check if any of the lib exists
306 result[lib[1]] = False
307 # if user want the name of the lib detected
309 result[lib[2]] = None
311 if conf.CheckLib(ll):
312 result[lib[1]] = True
315 cont += configString('#define %s 1' % lib[1], desc = description)
318 if not result[lib[1]]:
319 cont += configString('/* #undef %s */' % lib[1], desc = description)
321 for test in custom_tests:
323 result[test[1]] = True
325 cont += configString('#define %s 1' % test[1], desc = test[2])
327 cont += configString(test[3], desc = test[2])
329 result[test[1]] = False
331 cont += configString('/* #undef %s */' % test[1], desc = test[2])
333 cont += configString(test[4], desc = test[2])
334 # extra items (no key is returned)
335 for item in extra_items:
336 cont += configString(item[0], desc = item[1])
338 cont += '\n' + config_post + '\n'
340 writeToFile(config_file, cont)
344 def installCygwinLDScript(path):
345 ''' Install i386pe.x-no-rdata '''
346 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
347 script = open(ld_script, 'w')
348 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
350 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
351 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
353 OUTPUT_FORMAT(pei-i386)
354 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
355 ENTRY(_mainCRTStartup)
358 .text __image_base__ + __section_alignment__ :
365 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
366 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
367 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
368 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
370 /* ??? Why is .gcc_exc here? */
375 /* The Cygwin32 library uses a section to avoid copying certain data
376 on fork. This used to be named ".data". The linker used
377 to include this between __data_start__ and __data_end__, but that
378 breaks building the cygwin32 dll. Instead, we name the section
379 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
380 .data BLOCK(__section_alignment__) :
389 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
390 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
391 *(.rdata_runtime_pseudo_reloc)
392 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
393 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
395 *(.data_cygwin_nocopy)
397 .rdata BLOCK(__section_alignment__) :
400 .pdata BLOCK(__section_alignment__) :
404 .bss BLOCK(__section_alignment__) :
411 .edata BLOCK(__section_alignment__) :
422 .idata BLOCK(__section_alignment__) :
424 /* This cannot currently be handled with grouped sections.
425 See pe.em:sort_sections. */
428 /* These zeroes mark the end of the import list. */
429 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
435 .CRT BLOCK(__section_alignment__) :
437 ___crt_xc_start__ = . ;
438 *(SORT(.CRT$XC*)) /* C initialization */
439 ___crt_xc_end__ = . ;
440 ___crt_xi_start__ = . ;
441 *(SORT(.CRT$XI*)) /* C++ initialization */
442 ___crt_xi_end__ = . ;
443 ___crt_xl_start__ = . ;
444 *(SORT(.CRT$XL*)) /* TLS callbacks */
445 /* ___crt_xl_end__ is defined in the TLS Directory support code */
446 ___crt_xp_start__ = . ;
447 *(SORT(.CRT$XP*)) /* Pre-termination */
448 ___crt_xp_end__ = . ;
449 ___crt_xt_start__ = . ;
450 *(SORT(.CRT$XT*)) /* Termination */
451 ___crt_xt_end__ = . ;
453 .tls BLOCK(__section_alignment__) :
461 .endjunk BLOCK(__section_alignment__) :
463 /* end is deprecated, don't use it */
468 .rsrc BLOCK(__section_alignment__) :
473 .reloc BLOCK(__section_alignment__) :
477 .stab BLOCK(__section_alignment__) (NOLOAD) :
481 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
485 /* DWARF debug sections.
486 Symbols in the DWARF debugging sections are relative to the beginning
487 of the section. Unlike other targets that fake this by putting the
488 section VMA at 0, the PE format will not allow it. */
489 /* DWARF 1.1 and DWARF 2. */
490 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
494 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
499 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
501 *(.debug_info) *(.gnu.linkonce.wi.*)
503 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
507 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
511 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
515 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
519 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
523 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
527 /* SGI/MIPS DWARF 2 extensions. */
528 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
532 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
536 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
540 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
545 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
556 # these will be used under win32
562 # does not matter if it fails on other systems
567 def __init__(self, env, logfile, longarg, info):
568 # save the spawn system
570 self.logfile = logfile
571 # clear the logfile (it may not exist)
573 # this will overwrite existing content.
574 writeToFile(logfile, info, append=False)
576 self.longarg = longarg
577 # get hold of the old spawn? (necessary?)
578 self._spawn = env['SPAWN']
581 def spawn(self, sh, escape, cmd, args, spawnenv):
583 newargs = ' '.join(map(escape, args[1:]))
584 cmdline = cmd + " " + newargs
586 # if log is not empty, write to it
587 if self.logfile != '':
588 # this tend to be slow (?) but ensure correct output
589 # Note that cmdline may be long so I do not escape it
591 # since this is not an essential operation, proceed if things go wrong here.
592 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
594 print "Warning: can not write to log file ", self.logfile
596 # if the command is not too long, use the old
597 if not self.longarg or len(cmdline) < 8000:
598 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
600 sAttrs = win32security.SECURITY_ATTRIBUTES()
601 StartupInfo = win32process.STARTUPINFO()
603 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
604 # check for any special operating system commands
607 win32file.DeleteFile(arg)
610 # otherwise execute the command.
611 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
612 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
613 exit_code = win32process.GetExitCodeProcess(hProcess)
614 win32file.CloseHandle(hProcess);
615 win32file.CloseHandle(hThread);
619 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
620 ''' This function modify env and allow logging of
621 commands to a logfile. If the argument is too long
622 a win32 spawn will be used instead of the system one
625 # create a new spwn object
626 ls = loggedSpawn(env, logfile, longarg, info)
627 # replace the old SPAWN by the new function
628 env['SPAWN'] = ls.spawn