]> git.lyx.org Git - lyx.git/blob - development/scons/scons_utils.py
add qt3 support
[lyx.git] / development / scons / scons_utils.py
1 # vi:filetype=python:expandtab:tabstop=2:shiftwidth=2
2 #
3 # file scons_utils.py
4 #
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7 #
8 # \author Bo Peng
9 # Full author contact details are available in file CREDITS.
10 #
11 # This file defines all the utility functions for the
12 # scons-based build system of lyx
13 #
14
15 import os, sys, re, shutil, glob
16 from SCons.Util import WhereIs
17
18
19 def writeToFile(filename, lines, append = False):
20   " utility function: write or append lines to filename "
21   if append:
22     file = open(filename, 'a')
23   else:
24     file = open(filename, 'w')
25   file.write(lines)
26   file.close()
27
28
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
32
33     %key% (not key itself) is an indication of substitution
34   '''
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")
39
40   contents = source_file.read()
41   for k, v in env.items():
42     try:
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)
50     except:
51       pass
52   target_file.write(contents + "\n")
53   target_file.close()
54   #st = os.stat(str(source[0]))
55   #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
56
57
58 #
59 # glob filenames
60 #
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 = include + filter(lambda x: x not in exclude + include, glob.glob1(dir, pattern))
65   if build_dir is None:
66     return files
67   else:
68     return ['%s/%s' % (build_dir, x) for x in files]
69
70 #
71 # autoconf tests
72 #
73
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]
78   conf.Result(ret)
79   return ret
80
81
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]
86   conf.Result(ret)
87   return ret
88
89
90 def checkMkdirOneArg(conf):
91   check_mkdir_one_arg_source = """
92 #include <sys/stat.h>
93 int main()
94 {
95   mkdir("somedir");
96 }
97 """
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')
102   if ret:
103     conf.Result('one')
104   else:
105     conf.Result('two')
106   return ret
107
108
109 def checkCXXGlobalCstd(conf):
110   ''' Check the use of std::tolower or tolower '''
111   check_global_cstd_source = '''
112 #include <cctype>
113 using std::tolower;
114 int main()
115 {
116   return 0;
117 }
118 '''
119   conf.Message('Check for the use of global cstd... ')
120   ret = conf.TryLink(check_global_cstd_source, '.c')
121   conf.Result(ret)
122   return ret
123
124
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>
134 #endif
135 #if HAVE_SYS_SOCKET_H
136 # include <sys/socket.h>
137 #endif
138 extern int select (%s, %s, %s, %s, %s);
139 int main()
140 {
141   return(0);
142 }
143 ''' % (arg1, arg234, arg234, arg234, arg5)
144         ret = conf.TryLink(check_select_source, '.c')
145         if ret:
146           conf.Result(ret)
147           return (arg1, arg234, arg5)
148   conf.Result('no (use default)')
149   return ('int', 'int *', 'struct timeval *')
150
151
152 def checkBoostLibraries(conf, lib, pathes):
153   ''' look for boost libraries '''
154   conf.Message('Checking for boost library %s... ' % lib)
155   for path in pathes:
156     # direct form: e.g. libboost_iostreams.a
157     if os.path.isfile(os.path.join(path, 'lib%s.a' % lib)):
158       conf.Result('yes')
159       return (path, 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.
164     if len(files) >= 1:
165       # get xxx-gcc from /usr/local/lib/libboost_xxx-gcc.a
166       conf.Result('yes')
167       return (path, files[0].split(os.sep)[-1][3:-2])
168   conf.Result('n')
169   return ('','')
170
171
172 def checkCommand(conf, cmd):
173   ''' check the existence of a command
174     return full path to the command, or none
175   '''
176   conf.Message('Checking for command %s...' % cmd)
177   res = WhereIs(cmd)
178   conf.Result(res is not None)
179   return res
180
181
182 def checkLC_MESSAGES(conf):
183   ''' check the definition of LC_MESSAGES '''
184   check_LC_MESSAGES = '''
185 #include <locale.h>
186 int main()
187 {
188   return LC_MESSAGES;
189 }
190 '''
191   conf.Message('Check for LC_MESSAGES in locale.h... ')
192   ret = conf.TryLink(check_LC_MESSAGES, '.c')
193   conf.Result(ret)
194   return ret
195
196
197 def checkIconvConst(conf):
198   ''' check the declaration of iconv '''
199   check_iconv_const = '''
200 #include <stdlib.h>
201 #include <iconv.h>
202 extern
203 #ifdef __cplusplus
204 "C"
205 #endif
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);
209  #endif
210 #else
211 size_t iconv();
212 #endif
213
214 int main()
215 {
216   return 1;
217 }
218 '''
219   conf.Message('Check if the declaration of iconv needs const... ')
220   ret = conf.TryLink(check_iconv_const, '.c')
221   conf.Result(ret)
222   return ret
223
224
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 */'
247       will be used.
248     extra_items: extra configuration lines, in the form of a list of
249       ('config', 'description')
250   Return:
251     The result of each test, as a dictioanry of
252       res['XXX'] = True/False
253     XXX are keys defined in each argument.
254   '''
255   cont = config_pre + '\n'
256   result = {}
257   # add to this string, in appropriate format
258   def configString(lines, desc=''):
259     text = ''
260     if lines.strip() != '':
261       if desc != '':
262         text += '/* ' + desc + ' */\n'
263       text += lines + '\n\n'
264     return text
265   #
266   # headers
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)
273     else:
274       result[header[1]] = False
275       cont += configString('/* #undef %s */' % header[1], desc = description)
276   # functions
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)
282     else:
283       result[func[1]] = False
284       cont += configString('/* #undef %s */' % func[1], desc = description)
285   # types
286   for t in types:
287     description = "Define to 1 if you have the `%s' type." % t[0]
288     if conf.CheckType(t[0], includes=t[2]):
289       result[t[1]] = True
290       cont += configString('#define %s 1' % t[1], desc = description)
291     else:
292       result[t[1]] = False
293       cont += configString('/* #undef %s */' % t[1],  desc = description)
294   # libraries
295   for lib in libs:
296     description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
297     if type(lib[0]) is type(''):
298       lib_list = [lib[0]]
299     else:
300       lib_list = lib[0]
301     # check if any of the lib exists
302     result[lib[1]] = False
303     # if user want the name of the lib detected
304     if len(lib) == 3:
305       result[lib[2]] = None
306     for ll in lib_list:
307       if conf.CheckLib(ll):
308         result[lib[1]] = True
309         if len(lib) == 3:
310           result[lib[2]] = ll
311         cont += configString('#define %s 1' % lib[1], desc = description)
312         break
313     # if not found        
314     if not result[lib[1]]:
315       cont += configString('/* #undef %s */' % lib[1], desc = description)
316   # custom tests
317   for test in custom_tests:
318     if test[0]:
319       result[test[1]] = True
320       if len(test) == 3:
321         cont += configString('#define %s 1' % test[1], desc = test[2])
322       else:
323         cont += configString(test[3], desc = test[2])
324     else:
325       result[test[1]] = False
326       if len(test) == 3:
327         cont += configString('/* #undef %s */' % test[1], desc = test[2])
328       else:
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])
333   # add the last part
334   cont += '\n' + config_post + '\n'
335   # write to file
336   writeToFile(config_file, cont)
337   return result
338
339
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
345 for a reference see
346 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
347 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
348 */
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)
352 SECTIONS
353 {
354   .text  __image_base__ + __section_alignment__  :
355   {
356      *(.init)
357     *(.text)
358     *(SORT(.text$*))
359     *(.glue_7t)
360     *(.glue_7)
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);
365      *(.fini)
366     /* ??? Why is .gcc_exc here?  */
367      *(.gcc_exc)
368     PROVIDE (etext = .);
369     *(.gcc_except_table)
370   }
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__) :
377   {
378     __data_start__ = . ;
379     *(.data)
380     *(.data2)
381     *(SORT(.data$*))
382     *(.rdata)
383     *(SORT(.rdata$*))
384     *(.eh_frame)
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__ = .;
390     __data_end__ = . ;
391     *(.data_cygwin_nocopy)
392   }
393   .rdata BLOCK(__section_alignment__) :
394   {
395   }
396   .pdata BLOCK(__section_alignment__) :
397   {
398     *(.pdata)
399   }
400   .bss BLOCK(__section_alignment__) :
401   {
402     __bss_start__ = . ;
403     *(.bss)
404     *(COMMON)
405     __bss_end__ = . ;
406   }
407   .edata BLOCK(__section_alignment__) :
408   {
409     *(.edata)
410   }
411   /DISCARD/ :
412   {
413     *(.debug$S)
414     *(.debug$T)
415     *(.debug$F)
416     *(.drectve)
417   }
418   .idata BLOCK(__section_alignment__) :
419   {
420     /* This cannot currently be handled with grouped sections.
421         See pe.em:sort_sections.  */
422     SORT(*)(.idata$2)
423     SORT(*)(.idata$3)
424     /* These zeroes mark the end of the import list.  */
425     LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
426     SORT(*)(.idata$4)
427     SORT(*)(.idata$5)
428     SORT(*)(.idata$6)
429     SORT(*)(.idata$7)
430   }
431   .CRT BLOCK(__section_alignment__) :
432   {
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__ = . ;
448   }
449   .tls BLOCK(__section_alignment__) :
450   {
451     ___tls_start__ = . ;
452     *(.tls)
453     *(.tls$)
454     *(SORT(.tls$*))
455     ___tls_end__ = . ;
456   }
457   .endjunk BLOCK(__section_alignment__) :
458   {
459     /* end is deprecated, don't use it */
460     PROVIDE (end = .);
461     PROVIDE ( _end = .);
462      __end__ = .;
463   }
464   .rsrc BLOCK(__section_alignment__) :
465   {
466     *(.rsrc)
467     *(SORT(.rsrc$*))
468   }
469   .reloc BLOCK(__section_alignment__) :
470   {
471     *(.reloc)
472   }
473   .stab BLOCK(__section_alignment__) (NOLOAD) :
474   {
475     *(.stab)
476   }
477   .stabstr BLOCK(__section_alignment__) (NOLOAD) :
478   {
479     *(.stabstr)
480   }
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) :
487   {
488     *(.debug_aranges)
489   }
490   .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
491   {
492     *(.debug_pubnames)
493   }
494   /* DWARF 2.  */
495   .debug_info BLOCK(__section_alignment__) (NOLOAD) :
496   {
497     *(.debug_info) *(.gnu.linkonce.wi.*)
498   }
499   .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
500   {
501     *(.debug_abbrev)
502   }
503   .debug_line BLOCK(__section_alignment__) (NOLOAD) :
504   {
505     *(.debug_line)
506   }
507   .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
508   {
509     *(.debug_frame)
510   }
511   .debug_str BLOCK(__section_alignment__) (NOLOAD) :
512   {
513     *(.debug_str)
514   }
515   .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
516   {
517     *(.debug_loc)
518   }
519   .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
520   {
521     *(.debug_macinfo)
522   }
523   /* SGI/MIPS DWARF 2 extensions.  */
524   .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
525   {
526     *(.debug_weaknames)
527   }
528   .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
529   {
530     *(.debug_funcnames)
531   }
532   .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
533   {
534     *(.debug_typenames)
535   }
536   .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
537   {
538     *(.debug_varnames)
539   }
540   /* DWARF 3.  */
541   .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
542   {
543     *(.debug_ranges)
544   }
545 }
546 ''')
547   script.close()
548   return(ld_script)
549
550
551 try:
552   # these will be used under win32
553   import win32file
554   import win32event
555   import win32process
556   import win32security
557 except:
558   # does not matter if it fails on other systems
559   pass
560
561
562 class loggedSpawn:
563   def __init__(self, env, logfile, longarg, info):
564     # save the spawn system
565     self.env = env
566     self.logfile = logfile
567     # clear the logfile (it may not exist)
568     if logfile != '':
569       # this will overwrite existing content.
570       writeToFile(logfile, info, append=False)
571     #
572     self.longarg = longarg
573     # get hold of the old spawn? (necessary?)
574     self._spawn = env['SPAWN']
575
576   # define new SPAWN
577   def spawn(self, sh, escape, cmd, args, spawnenv):
578     # get command line
579     newargs = ' '.join(map(escape, args[1:]))
580     cmdline = cmd + " " + newargs
581     #
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
586       try:
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)
589       except:
590         print "Warning: can not write to log file ", self.logfile
591     #
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)
595     else:
596       sAttrs = win32security.SECURITY_ATTRIBUTES()
597       StartupInfo = win32process.STARTUPINFO()
598       for var in spawnenv:
599         spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
600       # check for any special operating system commands
601       if cmd == 'del':
602         for arg in args[1:]:
603           win32file.DeleteFile(arg)
604         exit_code = 0
605       else:
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);
612     return exit_code
613
614
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
619   '''
620   #
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
625