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 "
22 file = open(filename, 'a')
24 file = open(filename, 'w')
29 def env_subst(target, source, env):
30 ''' subst variables in source by those in env, and output to target
31 source and target are scons File() objects
33 %key% (not key itself) is an indication of substitution
35 assert len(target) == 1
36 assert len(source) == 1
37 target_file = file(str(target[0]), "w")
38 source_file = file(str(source[0]), "r")
40 contents = source_file.read()
41 for k, v in env.items():
43 val = env.subst('$'+k)
44 # temporary fix for the \Resource backslash problem
45 val = val.replace('\\', '/')
46 # multi-line replacement
47 val = val.replace('\n',r'\\n\\\n')
48 contents = re.sub('@'+k+'@', val, contents)
49 contents = re.sub('%'+k+'%', val, contents)
52 target_file.write(contents + "\n")
54 #st = os.stat(str(source[0]))
55 #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
61 def globSource(dir, pattern, build_dir=None, exclude=[], include=[]):
62 ''' glob files, in dir and use build_dir as returned path name '''
63 # exclude 'exclude+include' to avoid duplicate items in files
64 files = filter(lambda x: x not in exclude + include, glob.glob1(dir, pattern)) + include
68 return ['%s/%s' % (build_dir, x) for x in files]
74 def checkPkgConfig(conf, version):
75 ''' Return false if pkg_config does not exist, or is too old '''
76 conf.Message('Checking for pkg-config...')
77 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
82 def checkPackage(conf, pkg):
83 ''' check if pkg is under the control of conf '''
84 conf.Message('Checking for package %s...' % pkg)
85 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
90 def checkMkdirOneArg(conf):
91 check_mkdir_one_arg_source = """
98 conf.Message('Checking for the number of args for mkdir... ')
99 ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
100 conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
101 conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
109 def checkCXXGlobalCstd(conf):
110 ''' Check the use of std::tolower or tolower '''
111 check_global_cstd_source = '''
119 conf.Message('Check for the use of global cstd... ')
120 ret = conf.TryLink(check_global_cstd_source, '.c')
125 def checkSelectArgType(conf):
126 ''' Adapted from autoconf '''
127 conf.Message('Checking for arg types for select... ')
128 for arg234 in ['fd_set *', 'int *', 'void *']:
129 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
130 for arg5 in ['struct timeval *', 'const struct timeval *']:
131 check_select_source = '''
132 #if HAVE_SYS_SELECT_H
133 # include <sys/select.h>
135 #if HAVE_SYS_SOCKET_H
136 # include <sys/socket.h>
138 extern int select (%s, %s, %s, %s, %s);
143 ''' % (arg1, arg234, arg234, arg234, arg5)
144 ret = conf.TryLink(check_select_source, '.c')
147 return (arg1, arg234, arg5)
148 conf.Result('no (use default)')
149 return ('int', 'int *', 'struct timeval *')
152 def checkBoostLibraries(conf, lib, pathes):
153 ''' look for boost libraries '''
154 conf.Message('Checking for boost library %s... ' % lib)
156 # direct form: e.g. libboost_iostreams.a
157 if os.path.isfile(os.path.join(path, 'lib%s.a' % lib)):
160 # check things like libboost_iostreams-gcc.a
161 files = glob.glob(os.path.join(path, 'lib%s-*.a' % lib))
162 # if there are more than one, choose the first one
163 # FIXME: choose the best one.
165 # get xxx-gcc from /usr/local/lib/libboost_xxx-gcc.a
167 return (path, files[0].split(os.sep)[-1][3:-2])
172 def checkCommand(conf, cmd):
173 ''' check the existence of a command
174 return full path to the command, or none
176 conf.Message('Checking for command %s...' % cmd)
178 conf.Result(res is not None)
182 def checkLC_MESSAGES(conf):
183 ''' check the definition of LC_MESSAGES '''
184 check_LC_MESSAGES = '''
191 conf.Message('Check for LC_MESSAGES in locale.h... ')
192 ret = conf.TryLink(check_LC_MESSAGES, '.c')
197 def checkIconvConst(conf):
198 ''' check the declaration of iconv '''
199 check_iconv_const = '''
206 #if defined(__STDC__) || defined(__cplusplus)
207 #ifndef LIBICONV_DLL_EXPORTED
208 size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
219 conf.Message('Check if the declaration of iconv needs const... ')
220 ret = conf.TryLink(check_iconv_const, '.c')
225 def createConfigFile(conf, config_file,
226 config_pre = '', config_post = '',
227 headers = [], functions = [], types = [], libs = [],
228 custom_tests = [], extra_items = []):
229 ''' create a configuration file, with options
230 config_file: which file to create
231 config_pre: first part of the config file
232 config_post: last part of the config file
233 headers: header files to check, in the form of a list of
234 ('file', 'HAVE_FILE', 'c'/'c++')
235 functions: functions to check, in the form of a list of
236 ('func', 'HAVE_func', 'include lines'/None)
237 types: types to check, in the form of a list of
238 ('type', 'HAVE_TYPE', 'includelines'/None)
239 libs: libraries to check, in the form of a list of
240 ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
241 or any of the libs exists if 'lib' is a list of libs.
242 Optionally, user can provide another key LIB_NAME, that will
243 be set to the detected lib (or None otherwise).
244 custom_tests: extra tests to perform, in the form of a list of
245 (test (True/False), 'key', 'desc', 'true config line', 'false config line')
246 If the last two are ignored, '#define key 1' '/*#undef key */'
248 extra_items: extra configuration lines, in the form of a list of
249 ('config', 'description')
251 The result of each test, as a dictioanry of
252 res['XXX'] = True/False
253 XXX are keys defined in each argument.
255 cont = config_pre + '\n'
257 # add to this string, in appropriate format
258 def configString(lines, desc=''):
260 if lines.strip() != '':
262 text += '/* ' + desc + ' */\n'
263 text += lines + '\n\n'
267 for header in headers:
268 description = "Define to 1 if you have the <%s> header file." % header[0]
269 if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
270 (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
271 result[header[1]] = True
272 cont += configString('#define %s 1' % header[1], desc = description)
274 result[header[1]] = False
275 cont += configString('/* #undef %s */' % header[1], desc = description)
277 for func in functions:
278 description = "Define to 1 if you have the `%s' function." % func[0]
279 if conf.CheckFunc(func[0], header=func[2]):
280 result[func[1]] = True
281 cont += configString('#define %s 1' % func[1], desc = description)
283 result[func[1]] = False
284 cont += configString('/* #undef %s */' % func[1], desc = description)
287 description = "Define to 1 if you have the `%s' type." % t[0]
288 if conf.CheckType(t[0], includes=t[2]):
290 cont += configString('#define %s 1' % t[1], desc = description)
293 cont += configString('/* #undef %s */' % t[1], desc = description)
296 description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
297 if type(lib[0]) is type(''):
301 # check if any of the lib exists
302 result[lib[1]] = False
303 # if user want the name of the lib detected
305 result[lib[2]] = None
307 if conf.CheckLib(ll):
308 result[lib[1]] = True
311 cont += configString('#define %s 1' % lib[1], desc = description)
314 if not result[lib[1]]:
315 cont += configString('/* #undef %s */' % lib[1], desc = description)
317 for test in custom_tests:
319 result[test[1]] = True
321 cont += configString('#define %s 1' % test[1], desc = test[2])
323 cont += configString(test[3], desc = test[2])
325 result[test[1]] = False
327 cont += configString('/* #undef %s */' % test[1], desc = test[2])
329 cont += configString(test[4], desc = test[2])
330 # extra items (no key is returned)
331 for item in extra_items:
332 cont += configString(item[0], desc = item[1])
334 cont += '\n' + config_post + '\n'
336 writeToFile(config_file, cont)
340 def installCygwinLDScript(path):
341 ''' Install i386pe.x-no-rdata '''
342 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
343 script = open(ld_script, 'w')
344 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
346 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
347 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
349 OUTPUT_FORMAT(pei-i386)
350 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
351 ENTRY(_mainCRTStartup)
354 .text __image_base__ + __section_alignment__ :
361 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
362 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
363 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
364 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
366 /* ??? Why is .gcc_exc here? */
371 /* The Cygwin32 library uses a section to avoid copying certain data
372 on fork. This used to be named ".data". The linker used
373 to include this between __data_start__ and __data_end__, but that
374 breaks building the cygwin32 dll. Instead, we name the section
375 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
376 .data BLOCK(__section_alignment__) :
385 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
386 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
387 *(.rdata_runtime_pseudo_reloc)
388 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
389 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
391 *(.data_cygwin_nocopy)
393 .rdata BLOCK(__section_alignment__) :
396 .pdata BLOCK(__section_alignment__) :
400 .bss BLOCK(__section_alignment__) :
407 .edata BLOCK(__section_alignment__) :
418 .idata BLOCK(__section_alignment__) :
420 /* This cannot currently be handled with grouped sections.
421 See pe.em:sort_sections. */
424 /* These zeroes mark the end of the import list. */
425 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
431 .CRT BLOCK(__section_alignment__) :
433 ___crt_xc_start__ = . ;
434 *(SORT(.CRT$XC*)) /* C initialization */
435 ___crt_xc_end__ = . ;
436 ___crt_xi_start__ = . ;
437 *(SORT(.CRT$XI*)) /* C++ initialization */
438 ___crt_xi_end__ = . ;
439 ___crt_xl_start__ = . ;
440 *(SORT(.CRT$XL*)) /* TLS callbacks */
441 /* ___crt_xl_end__ is defined in the TLS Directory support code */
442 ___crt_xp_start__ = . ;
443 *(SORT(.CRT$XP*)) /* Pre-termination */
444 ___crt_xp_end__ = . ;
445 ___crt_xt_start__ = . ;
446 *(SORT(.CRT$XT*)) /* Termination */
447 ___crt_xt_end__ = . ;
449 .tls BLOCK(__section_alignment__) :
457 .endjunk BLOCK(__section_alignment__) :
459 /* end is deprecated, don't use it */
464 .rsrc BLOCK(__section_alignment__) :
469 .reloc BLOCK(__section_alignment__) :
473 .stab BLOCK(__section_alignment__) (NOLOAD) :
477 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
481 /* DWARF debug sections.
482 Symbols in the DWARF debugging sections are relative to the beginning
483 of the section. Unlike other targets that fake this by putting the
484 section VMA at 0, the PE format will not allow it. */
485 /* DWARF 1.1 and DWARF 2. */
486 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
490 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
495 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
497 *(.debug_info) *(.gnu.linkonce.wi.*)
499 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
503 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
507 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
511 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
515 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
519 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
523 /* SGI/MIPS DWARF 2 extensions. */
524 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
528 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
532 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
536 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
541 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
552 # these will be used under win32
558 # does not matter if it fails on other systems
563 def __init__(self, env, logfile, longarg, info):
564 # save the spawn system
566 self.logfile = logfile
567 # clear the logfile (it may not exist)
569 # this will overwrite existing content.
570 writeToFile(logfile, info, append=False)
572 self.longarg = longarg
573 # get hold of the old spawn? (necessary?)
574 self._spawn = env['SPAWN']
577 def spawn(self, sh, escape, cmd, args, spawnenv):
579 newargs = ' '.join(map(escape, args[1:]))
580 cmdline = cmd + " " + newargs
582 # if log is not empty, write to it
583 if self.logfile != '':
584 # this tend to be slow (?) but ensure correct output
585 # Note that cmdline may be long so I do not escape it
587 # since this is not an essential operation, proceed if things go wrong here.
588 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
590 print "Warning: can not write to log file ", self.logfile
592 # if the command is not too long, use the old
593 if not self.longarg or len(cmdline) < 8000:
594 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
596 sAttrs = win32security.SECURITY_ATTRIBUTES()
597 StartupInfo = win32process.STARTUPINFO()
599 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
600 # check for any special operating system commands
603 win32file.DeleteFile(arg)
606 # otherwise execute the command.
607 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
608 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
609 exit_code = win32process.GetExitCodeProcess(hProcess)
610 win32file.CloseHandle(hProcess);
611 win32file.CloseHandle(hThread);
615 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
616 ''' This function modify env and allow logging of
617 commands to a logfile. If the argument is too long
618 a win32 spawn will be used instead of the system one
621 # create a new spwn object
622 ls = loggedSpawn(env, logfile, longarg, info)
623 # replace the old SPAWN by the new function
624 env['SPAWN'] = ls.spawn