]> git.lyx.org Git - lyx.git/blob - development/scons/scons_utils.py
listerrors.lyx : Update a link.
[lyx.git] / development / scons / scons_utils.py
1 # vi:filetype=python:expandtab:tabstop=4:shiftwidth=4
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 *
17
18
19 def getVerFromConfigure(path):
20     ''' get lyx version from the AC_INIT line of configure.ac,
21         packed major and minor version numbers from the lyx version,
22         and LYX_DATE from an AC_SUBST line.
23     '''
24     try:
25         config = open(os.path.join(path, 'configure.ac'))
26     except:
27         print "Can not open configure.ac. "
28         return 'x.x.x'
29     # find a line like follows
30     # AC_INIT(LyX,1.4.4svn,[lyx-devel@lists.lyx.org],[lyx])
31     ver_pat = re.compile('AC_INIT\([^,]+,([^,]+),')
32     date_pat = re.compile('AC_SUBST\(LYX_DATE, \["(.*)"\]\)')
33     majmin_pat = re.compile('(\d+)\.(\d+)\..*')
34     version = 'x.x.x'
35     majmin = 'xx'
36     date = 'Not released'
37     for line in config.readlines():
38         if ver_pat.match(line):
39             (version,) = ver_pat.match(line).groups()
40             majmin_match = majmin_pat.match(version)
41             majmin = majmin_match.group(1) + majmin_match.group(2)
42         if date_pat.match(line):
43             (date,) = date_pat.match(line).groups()
44         if version != 'x.x.x' and date != 'Not released':
45             break
46     return version.strip(), majmin.strip(), date.strip()
47
48
49 def relativePath(path, base):
50     '''return relative path from base, which is usually top source dir'''
51     # full pathname of path
52     path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
53     path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
54     if path1[:len(path2)] != path2:
55         print "Path %s is not under top source directory" % path
56     if len(path2) == len(path1):
57         return ''
58     path3 = os.path.join(*path1[len(path2):]);
59     # replace all \ by / such that we get the same comments on Windows and *nix
60     path3 = path3.replace('\\', '/')
61     return path3
62
63
64 def isSubDir(path, base):
65     '''Whether or not path is a subdirectory of base'''
66     path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
67     path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
68     return len(path2) <= len(path1) and path1[:len(path2)] == path2
69
70
71 def writeToFile(filename, lines, append = False):
72     " utility function: write or append lines to filename "
73     # create directory if needed
74     dir = os.path.split(filename)[0]
75     if dir != '' and not os.path.isdir(dir):
76         os.makedirs(dir)
77     if append:
78         file = open(filename, 'a')
79     else:
80         file = open(filename, 'w')
81     file.write(lines)
82     file.close()
83
84
85 def env_subst(target, source, env):
86     ''' subst variables in source by those in env, and output to target
87         source and target are scons File() objects
88
89         %key% (not key itself) is an indication of substitution
90     '''
91     assert len(target) == 1
92     assert len(source) == 1
93     target_file = file(str(target[0]), "w")
94     source_file = file(str(source[0]), "r")
95
96     contents = source_file.read()
97     for k, v in env.items():
98         try:
99             val = env.subst('$'+k)
100             # temporary fix for the \Resource backslash problem
101             val = val.replace('\\', '/')
102             # multi-line replacement
103             val = val.replace('\n',r'\\n\\\n')
104             contents = re.sub('@'+k+'@', val, contents)
105         except:
106             pass
107     target_file.write(contents + "\n")
108     target_file.close()
109     #st = os.stat(str(source[0]))
110     #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
111
112
113 def env_nsis(source, target, env, for_signature):
114     ''' Get nsis command line '''
115     def quoteIfSpaced(str):
116         if ' ' in str:
117             return '"' + str + '"'
118         else:
119             return str
120     ret = env['NSIS'] + " /V1 "
121     if env.has_key('NSISFLAGS'):
122         for flag in env['NSISFLAGS']:
123             ret += flag
124             ret += ' '
125     if env.has_key('NSISDEFINES'):
126         for d in env['NSISDEFINES']:
127             ret += '/D'+d
128             if env['NSISDEFINES'][d]:
129                 ret += '=' + quoteIfSpaced(env['NSISDEFINES'][d])
130             ret += ' '
131     # bundled?
132     if '-bundle.exe' in str(target[0]):
133         ret += '/DSETUPTYPE=BUNDLE '
134     for s in source:
135         ret += quoteIfSpaced(str(s))
136     return ret
137
138
139 def env_toc(target, source, env):
140     '''Generate target from source files'''
141     # this is very tricky because we need to use installed lyx2lyx with 
142     # correct lyx2lyx_version.py
143     sys.path.append(env['LYX2LYX_DEST'])
144     sys.path.append(env.Dir('$TOP_SRCDIR/lib/doc').abspath)
145     import doc_toc
146     # build toc
147     doc_toc.build_toc(str(target[0]), [file.abspath for file in source])
148     
149     
150 def env_cat(target, source, env):
151     '''Cat source > target. Avoid pipe to increase portability'''
152     output = open(env.File(target[0]).abspath, 'w')
153     for src in source:
154         input = open(env.File(src).abspath)
155         output.write(input.read())
156         input.close()
157     output.close()
158
159
160 def env_potfiles(target, source, env):
161     '''Build po/POTFILES.in'''
162     # command 
163     #   grep -l '_(\".*\")' `find src \( -name '*.h' -o -name '*.cpp' -o -name '*.cpp.in' \) -print` | grep -v -e "src/support/Package.cpp$$" | sort | uniq
164     # is used under *nix but windows users have to do these all in python
165     target_file = open(str(target[0]), "w")
166     potfiles = []
167     trans = re.compile('_\(".*"\)', re.M)
168     for file in source:
169         rel_file = relativePath(str(file), env.subst('$TOP_SRCDIR'))
170         if rel_file not in potfiles and trans.search(open(str(file)).read()):
171             potfiles.append(rel_file)
172     potfiles.sort()
173     print >> target_file, '\n'.join(potfiles)
174     target_file.close()
175
176     
177 def createResFromIcon(env, icon_file, rc_file):
178     ''' create a rc file with icon, and return res file (windows only) '''
179     if os.name == 'nt':
180         rc_name = env.File(rc_file).abspath
181         dir = os.path.split(rc_name)[0]
182         if not os.path.isdir(dir):
183             os.makedirs(dir)
184         rc = open(rc_name, 'w')
185         print >> rc, 'IDI_ICON1  ICON DISCARDABLE "%s"' % \
186             os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'development', 'win32',
187                 'packaging', 'icons', icon_file).replace('\\', '\\\\')
188         rc.close()
189         return env.RES(rc_name)
190     else:
191         return []
192
193
194 def env_qtResource(target, source, env):
195     '''Create resource.qrc'''
196     qrc = open(str(target[0]), 'w')
197     print >> qrc, "<!DOCTYPE RCC><RCC version='1.0'><qresource>"
198     for file in source:
199         rel_file = relativePath(str(file), env.subst('$TOP_SRCDIR/lib'))
200         abs_file = str(file.abspath)
201         print >> qrc, '<file alias="%s">%s</file>' % (rel_file, abs_file)
202     print >> qrc, '</qresource></RCC>'
203     qrc.close()
204
205 #
206 # autoconf tests
207 #
208
209 def checkPkgConfig(conf, version):
210     ''' Return false if pkg_config does not exist, or is too old '''
211     conf.Message('Checking for pkg-config...')
212     ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
213     conf.Result(ret)
214     return ret
215
216
217 def checkPackage(conf, pkg):
218     ''' check if pkg is under the control of conf '''
219     conf.Message('Checking for package %s...' % pkg)
220     ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
221     conf.Result(ret)
222     return ret
223
224
225 def checkMkdirOneArg(conf):
226     check_mkdir_one_arg_source = """
227 #include <sys/stat.h>
228 int main()
229 {
230     mkdir("somedir");
231 }
232 """
233     conf.Message('Checking for the number of args for mkdir... ')
234     ret = conf.TryLink(check_mkdir_one_arg_source, '.c') or \
235         conf.TryLink('#include <unistd.h>' + check_mkdir_one_arg_source, '.c') or \
236         conf.TryLink('#include <direct.h>' + check_mkdir_one_arg_source, '.c')
237     if ret:
238         conf.Result('one')
239     else:
240         conf.Result('two')
241     return ret
242
243
244 def checkCXXGlobalCstd(conf):
245     ''' Checking the use of std::tolower or tolower '''
246     check_global_cstd_source = '''
247 #include <cctype>
248 using std::tolower;
249 int main()
250 {
251     return 0;
252 }
253 '''
254     conf.Message('Checking for the use of global cstd... ')
255     # if can not compile, define CXX_GLOBAL_CSTD
256     ret = conf.TryLink(check_global_cstd_source, '.cpp')
257     conf.Result(ret)
258     return ret
259
260
261 def checkSelectArgType(conf):
262     ''' Adapted from autoconf '''
263     conf.Message('Checking for arg types for select... ')
264     for arg234 in ['fd_set *', 'int *', 'void *']:
265         for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
266             for arg5 in ['struct timeval *', 'const struct timeval *']:
267                 check_select_source = '''
268 #if HAVE_SYS_SELECT_H
269 # include <sys/select.h>
270 #endif
271 #if HAVE_SYS_SOCKET_H
272 # include <sys/socket.h>
273 #endif
274 extern int select (%s, %s, %s, %s, %s);
275 int main()
276 {
277     return(0);
278 }
279 ''' % (arg1, arg234, arg234, arg234, arg5)
280                 ret = conf.TryLink(check_select_source, '.c')
281                 if ret:
282                     conf.Result(ret)
283                     return (arg1, arg234, arg5)
284     conf.Result('no (use default)')
285     return ('int', 'int *', 'struct timeval *')
286
287
288 def checkBoostLibraries(conf, libs, lib_paths, inc_paths, versions, isDebug):
289     ''' look for boost libraries
290       libs: library names
291       lib_paths: try these paths for boost libraries
292       inc_paths: try these paths for boost headers
293       versions:   supported boost versions
294       isDebug:   if true, use debug libraries
295     '''
296     conf.Message('Checking for boost library %s... ' % ', '.join(libs))
297     libprefix = conf.env['LIBPREFIX']
298     libsuffix = '(%s|%s)' % (conf.env['LIBSUFFIX'], conf.env['SHLIBSUFFIX'])
299     found_lib = False
300     found_inc = False
301     lib_names = []
302     lib_path = None
303     inc_path = None
304     for path in lib_paths:
305         conf.Log("Looking into %s\n" % path)
306         for lib in libs:
307             # get all the libs, then filter for the right library
308             files = glob.glob(os.path.join(path, '%sboost_%s-*.*' % (libprefix, lib)))
309             # check things like libboost_iostreams-gcc-mt-d-1_33_1.a
310             if len(files) > 0:
311                 conf.Log("Find boost libraries: %s\n" % files)
312                 # runtime code includes s,g,y,d,p,n, where we should look for
313                 # d,g,y for debug, s,p,n for release
314                 lib_files = []
315                 if isDebug:
316                     for ver in versions:
317                         lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-[^spn]+-%s%s' % (libprefix, lib, ver, libsuffix), x), files)
318                 else:
319                     for ver in versions:
320                         lib_files += filter(lambda x: re.search('%sboost_%s-\w+-mt-([^dgy]+-)*%s%s' % (libprefix, lib, ver, libsuffix), x), files)
321                 if len(lib_files) == 0:
322                     # use alternative libraries
323                     for ver in versions:
324                         lib_files += filter(lambda x: re.search('%sboost_%s-[\w-]+%s%s' % (libprefix, lib, ver, libsuffix), x), files)
325                 if len(lib_files) > 0:
326                     # get xxx-gcc-1_33_1 from /usr/local/lib/libboost_xxx-gcc-1_33_1.a
327                     name = lib_files[0].split(os.sep)[-1][len(libprefix):]
328                     lib_names.append(name.split('.')[0])
329                     conf.Log("Qualified libraries: %s\n" % lib_names)
330                 else:
331                     conf.Log("No qualified library is found.\n")
332                     break
333         if len(lib_names) == len(libs):
334             found_lib = True
335             lib_path = path
336             break
337     if not found_lib:
338         if len(lib_names) == 0:
339             conf.Log("No boost library is found\n")
340         else:
341             conf.Log("Found boost libraries: %s\n" % lib_names)
342         conf.Result('no')
343         return (None, None, None)
344     # check version number in boost/version.hpp
345     def isValidBoostDir(dir):
346         version_file = os.path.join(dir, 'boost', 'version.hpp')
347         if not os.path.isfile(version_file):
348             return False
349         version_file_content = open(version_file).read()
350         version_strings = ['#define BOOST_LIB_VERSION "%s"' % ver for ver in versions]
351         return True in [x in version_file_content for x in version_strings]
352     # check for boost header file
353     for path in inc_paths:
354         conf.Log("Checking for inc path: %s\n" % path)
355         if isValidBoostDir(path):
356             inc_path = path
357             found_inc = True
358         else:   # check path/boost_1_xx_x/boost
359             dirs = glob.glob(os.path.join(path, 'boost-*'))
360             if len(dirs) > 0 and isValidBoostDir(dirs[0]):
361                 conf.Log("Checing for sub directory: %s\n" % dirs[0])
362                 inc_path = dirs[0]
363                 found_inc = True
364     # return result
365     if found_inc:
366         conf.Result('yes')
367         conf.Log('Using boost libraries %s\n' % (', '.join(lib_names)))
368         return (lib_names, lib_path, inc_path)
369     else:
370         conf.Result('no')
371         return (None, None, None)
372
373
374 def checkCommand(conf, cmd):
375     ''' check the existence of a command
376         return full path to the command, or none
377     '''
378     conf.Message('Checking for command %s...' % cmd)
379     res = WhereIs(cmd)
380     conf.Result(res is not None)
381     return res
382
383
384 def checkNSIS(conf):
385     ''' check the existence of nsis compiler, return the fullpath '''
386     conf.Message('Checking for nsis compiler...')
387     res = None
388     if can_read_reg:
389         # If we can read the registry, get the NSIS command from it
390         try:
391             k = RegOpenKeyEx(hkey_mod.HKEY_LOCAL_MACHINE,
392                                   'SOFTWARE\\NSIS')
393             val, tok = RegQueryValueEx(k,None)
394             ret = val + os.path.sep + 'makensis.exe'
395             if os.path.isfile(ret):
396                 res = '"' + ret + '"'
397             else:
398                 res = None
399         except:
400             pass # Couldn't find the key, just act like we can't read the registry
401     # Hope it's on the path
402     if res is None:
403         res = WhereIs('makensis.exe')
404     conf.Result(res is not None)
405     return res
406
407
408 def checkLC_MESSAGES(conf):
409     ''' check the definition of LC_MESSAGES '''
410     check_LC_MESSAGES = '''
411 #include <locale.h>
412 int main()
413 {
414     return LC_MESSAGES;
415 }
416 '''
417     conf.Message('Checking for LC_MESSAGES in locale.h... ')
418     ret = conf.TryLink(check_LC_MESSAGES, '.c')
419     conf.Result(ret)
420     return ret
421
422
423 def checkIconvConst(conf):
424     ''' check the declaration of iconv '''
425     check_iconv_const = '''
426 #include <iconv.h>
427 // this declaration will fail when there already exists a non const char** 
428 // version which returns size_t
429 double iconv(iconv_t cd,  char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
430 int main() {
431     return 0; 
432 }
433 '''
434     conf.Message('Checking if the declaration of iconv needs const... ')
435     ret = conf.TryLink(check_iconv_const, '.cpp')
436     conf.Result(ret)
437     return ret
438
439
440 def checkSizeOfWChar(conf):
441     ''' check the size of wchar '''
442     check_sizeof_wchar = '''
443 #include <wchar.h>
444 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
445 int main()
446 {
447     return 0;
448 }
449 '''
450     conf.Message('Checking the size of wchar_t... ')
451     if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
452         ret = 2
453     elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
454         ret = 4
455     else:
456         ret = 0
457     conf.Result(str(ret))
458     return ret
459
460
461 def checkDeclaration(conf, func, headers):
462     ''' check if a function is declared in given headers '''
463     check_decl = '''
464 #include <%%s>
465 int main()
466 {
467 #ifndef %s
468     char *p = (char *) %s;
469 #endif
470 }
471 ''' % (func, func)
472     conf.Message('Checking for the declaration of function %s... ' % func)
473     ret = True in [conf.TryLink(check_decl % header, '.c') for header in headers]
474     conf.Result(ret)
475     return ret
476
477     
478 def createConfigFile(conf, config_file,
479     config_pre = '', config_post = '',
480     headers = [], functions = [], declarations = [], types = [], libs = [],
481     custom_tests = [], extra_items = []):
482     ''' create a configuration file, with options
483         config_file: which file to create
484         config_pre: first part of the config file
485         config_post: last part of the config file
486         headers: header files to check, in the form of a list of
487             ('file', 'HAVE_FILE', 'c'/'c++')
488         functions: functions to check, in the form of a list of
489             ('func', 'HAVE_func', 'include lines'/None)
490         declarations: function declarations to check, in the form of a list of
491             ('func', 'HAVE_DECL_func', header_files)
492         types: types to check, in the form of a list of
493             ('type', 'HAVE_TYPE', 'includelines'/None)
494         libs: libraries to check, in the form of a list of
495             ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
496             or any of the libs exists if 'lib' is a list of libs.
497             Optionally, user can provide another key LIB_NAME, that will
498             be set to the detected lib (or None otherwise).
499         custom_tests: extra tests to perform, in the form of a list of
500             (test (True/False), 'key', 'desc', 'true config line', 'false config line')
501             If the last two are ignored, '#define key 1' '/*#undef key */'
502             will be used.
503         extra_items: extra configuration lines, in the form of a list of
504             ('config', 'description')
505     Return:
506         The result of each test, as a dictioanry of
507             res['XXX'] = True/False
508         XXX are keys defined in each argument.
509     '''
510     cont = config_pre + '\n'
511     result = {}
512     # add to this string, in appropriate format
513     def configString(lines, desc=''):
514         text = ''
515         if lines.strip() != '':
516             if desc != '':
517                 text += '/* ' + desc + ' */\n'
518             text += lines + '\n\n'
519         return text
520     #
521     # headers
522     for header in headers:
523         description = "Define to 1 if you have the <%s> header file." % header[0]
524         if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
525             (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
526             result[header[1]] = 1
527             cont += configString('#define %s 1' % header[1], desc = description)
528         else:
529             result[header[1]] = 0
530             cont += configString('/* #undef %s */' % header[1], desc = description)
531     # functions
532     for func in functions:
533         description = "Define to 1 if you have the `%s' function." % func[0]
534         if conf.CheckFunc(func[0], header=func[2]):
535             result[func[1]] = 1
536             cont += configString('#define %s 1' % func[1], desc = description)
537         else:
538             result[func[1]] = 0
539             cont += configString('/* #undef %s */' % func[1], desc = description)
540     for decl in declarations:
541         description = "Define to 1 if you have the declaration of `%s', and to 0 if you don't." % decl[0]
542         if conf.CheckDeclaration(decl[0], decl[2]):
543             result[decl[1]] = 1
544             cont += configString('#define %s 1' % decl[1], desc = description)
545         else:
546             result[decl[1]] = 0
547             cont += configString('/* #undef %s */' % decl[1], desc = description)
548     # types
549     for t in types:
550         description = "Define to 1 if you have the `%s' type." % t[0]
551         if conf.CheckType(t[0], includes=t[2]):
552             result[t[1]] = 1
553             cont += configString('#define %s 1' % t[1], desc = description)
554         else:
555             result[t[1]] = 0
556             cont += configString('/* #undef %s */' % t[1],  desc = description)
557     # libraries
558     for lib in libs:
559         description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
560         if type(lib[0]) is type(''):
561             lib_list = [lib[0]]
562         else:
563             lib_list = lib[0]
564         # check if any of the lib exists
565         result[lib[1]] = 0
566         # if user want the name of the lib detected
567         if len(lib) == 3:
568             result[lib[2]] = None
569         for ll in lib_list:
570             if conf.CheckLib(ll):
571                 result[lib[1]] = 1
572                 if len(lib) == 3:
573                     result[lib[2]] = ll
574                 cont += configString('#define %s 1' % lib[1], desc = description)
575                 break
576         # if not found
577         if not result[lib[1]]:
578             cont += configString('/* #undef %s */' % lib[1], desc = description)
579     # custom tests
580     for test in custom_tests:
581         if test[0]:
582             result[test[1]] = 1
583             if len(test) == 3:
584                 cont += configString('#define %s 1' % test[1], desc = test[2])
585             else:
586                 cont += configString(test[3], desc = test[2])
587         else:
588             result[test[1]] = 0
589             if len(test) == 3:
590                 cont += configString('/* #undef %s */' % test[1], desc = test[2])
591             else:
592                 cont += configString(test[4], desc = test[2])
593     # extra items (no key is returned)
594     for item in extra_items:
595         cont += configString(item[0], desc = item[1])
596     # add the last part
597     cont += '\n' + config_post + '\n'
598     # write to file
599     writeToFile(config_file, cont)
600     return result
601
602
603 def installCygwinLDScript(path):
604     ''' Install i386pe.x-no-rdata '''
605     ld_script = os.path.join(path, 'i386pe.x-no-rdata')
606     script = open(ld_script, 'w')
607     script.write('''/* specific linker script avoiding .rdata sections, for normal executables
608 for a reference see
609 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
610 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
611 */
612 OUTPUT_FORMAT(pei-i386)
613 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
614 ENTRY(_mainCRTStartup)
615 SECTIONS
616 {
617   .text  __image_base__ + __section_alignment__  :
618   {
619     *(.init)
620     *(.text)
621     *(SORT(.text$*))
622     *(.glue_7t)
623     *(.glue_7)
624     ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
625                         LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*));  LONG (0);
626     ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
627                         LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*));  LONG (0);
628     *(.fini)
629     /* ??? Why is .gcc_exc here?  */
630     *(.gcc_exc)
631     PROVIDE (etext = .);
632     *(.gcc_except_table)
633   }
634   /* The Cygwin32 library uses a section to avoid copying certain data
635     on fork.  This used to be named ".data".  The linker used
636     to include this between __data_start__ and __data_end__, but that
637     breaks building the cygwin32 dll.  Instead, we name the section
638     ".data_cygwin_nocopy" and explictly include it after __data_end__. */
639   .data BLOCK(__section_alignment__) :
640   {
641     __data_start__ = . ;
642     *(.data)
643     *(.data2)
644     *(SORT(.data$*))
645     *(.rdata)
646     *(SORT(.rdata$*))
647     *(.eh_frame)
648     ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
649     __RUNTIME_PSEUDO_RELOC_LIST__ = .;
650     *(.rdata_runtime_pseudo_reloc)
651     ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
652     __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
653     __data_end__ = . ;
654     *(.data_cygwin_nocopy)
655   }
656   .rdata BLOCK(__section_alignment__) :
657   {
658   }
659   .pdata BLOCK(__section_alignment__) :
660   {
661     *(.pdata)
662   }
663   .bss BLOCK(__section_alignment__) :
664   {
665     __bss_start__ = . ;
666     *(.bss)
667     *(COMMON)
668     __bss_end__ = . ;
669   }
670   .edata BLOCK(__section_alignment__) :
671   {
672     *(.edata)
673   }
674   /DISCARD/ :
675   {
676     *(.debug$S)
677     *(.debug$T)
678     *(.debug$F)
679     *(.drectve)
680   }
681   .idata BLOCK(__section_alignment__) :
682   {
683     /* This cannot currently be handled with grouped sections.
684         See pe.em:sort_sections.  */
685     SORT(*)(.idata$2)
686     SORT(*)(.idata$3)
687     /* These zeroes mark the end of the import list.  */
688     LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
689     SORT(*)(.idata$4)
690     SORT(*)(.idata$5)
691     SORT(*)(.idata$6)
692     SORT(*)(.idata$7)
693   }
694   .CRT BLOCK(__section_alignment__) :
695   {
696     ___crt_xc_start__ = . ;
697     *(SORT(.CRT$XC*))  /* C initialization */
698     ___crt_xc_end__ = . ;
699     ___crt_xi_start__ = . ;
700     *(SORT(.CRT$XI*))  /* C++ initialization */
701     ___crt_xi_end__ = . ;
702     ___crt_xl_start__ = . ;
703     *(SORT(.CRT$XL*))  /* TLS callbacks */
704     /* ___crt_xl_end__ is defined in the TLS Directory support code */
705     ___crt_xp_start__ = . ;
706     *(SORT(.CRT$XP*))  /* Pre-termination */
707     ___crt_xp_end__ = . ;
708     ___crt_xt_start__ = . ;
709     *(SORT(.CRT$XT*))  /* Termination */
710     ___crt_xt_end__ = . ;
711   }
712   .tls BLOCK(__section_alignment__) :
713   {
714     ___tls_start__ = . ;
715     *(.tls)
716     *(.tls$)
717     *(SORT(.tls$*))
718     ___tls_end__ = . ;
719   }
720   .endjunk BLOCK(__section_alignment__) :
721   {
722     /* end is deprecated, don't use it */
723     PROVIDE (end = .);
724     PROVIDE ( _end = .);
725     __end__ = .;
726   }
727   .rsrc BLOCK(__section_alignment__) :
728   {
729     *(.rsrc)
730     *(SORT(.rsrc$*))
731   }
732   .reloc BLOCK(__section_alignment__) :
733   {
734     *(.reloc)
735   }
736   .stab BLOCK(__section_alignment__) (NOLOAD) :
737   {
738     *(.stab)
739   }
740   .stabstr BLOCK(__section_alignment__) (NOLOAD) :
741   {
742     *(.stabstr)
743   }
744   /* DWARF debug sections.
745     Symbols in the DWARF debugging sections are relative to the beginning
746     of the section.  Unlike other targets that fake this by putting the
747     section VMA at 0, the PE format will not allow it.  */
748   /* DWARF 1.1 and DWARF 2.  */
749   .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
750   {
751     *(.debug_aranges)
752   }
753   .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
754   {
755     *(.debug_pubnames)
756   }
757   /* DWARF 2.  */
758   .debug_info BLOCK(__section_alignment__) (NOLOAD) :
759   {
760     *(.debug_info) *(.gnu.linkonce.wi.*)
761   }
762   .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
763   {
764     *(.debug_abbrev)
765   }
766   .debug_line BLOCK(__section_alignment__) (NOLOAD) :
767   {
768     *(.debug_line)
769   }
770   .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
771   {
772     *(.debug_frame)
773   }
774   .debug_str BLOCK(__section_alignment__) (NOLOAD) :
775   {
776     *(.debug_str)
777   }
778   .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
779   {
780     *(.debug_loc)
781   }
782   .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
783   {
784     *(.debug_macinfo)
785   }
786   /* SGI/MIPS DWARF 2 extensions.  */
787   .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
788   {
789     *(.debug_weaknames)
790   }
791   .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
792   {
793     *(.debug_funcnames)
794   }
795   .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
796   {
797     *(.debug_typenames)
798   }
799   .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
800   {
801     *(.debug_varnames)
802   }
803   /* DWARF 3.  */
804   .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
805   {
806     *(.debug_ranges)
807   }
808 }
809 ''')
810     script.close()
811     return(ld_script)
812
813
814 def installCygwinPostinstallScript(path):
815     ''' Install lyx.sh '''
816     postinstall_script = os.path.join(path, 'lyx.sh')
817     script = open(postinstall_script, 'w')
818     script.write(r'''#!/bin/sh
819
820 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
821 # if it is not already there.
822 if [ -f /etc/fonts/local.conf ]; then
823     grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
824     if [ $? -ne 0 ]; then
825         sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
826         mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
827         fc-cache /usr/share/lyx/fonts
828     fi
829 fi
830     ''')
831     script.close()
832     return(postinstall_script)
833
834
835 try:
836     # these will be used under win32
837     import win32file
838     import win32event
839     import win32process
840     import win32security
841 except:
842     # does not matter if it fails on other systems
843     pass
844
845
846 class loggedSpawn:
847     def __init__(self, env, logfile, longarg, info):
848         # save the spawn system
849         self.env = env
850         self.logfile = logfile
851         # clear the logfile (it may not exist)
852         if logfile != '':
853             # this will overwrite existing content.
854             writeToFile(logfile, info, append=False)
855         #
856         self.longarg = longarg
857         # get hold of the old spawn? (necessary?)
858         self._spawn = env['SPAWN']
859
860     # define new SPAWN
861     def spawn(self, sh, escape, cmd, args, spawnenv):
862         # get command line
863         newargs = ' '.join(map(escape, args[1:]))
864         cmdline = cmd + " " + newargs
865         #
866         # if log is not empty, write to it
867         if self.logfile != '':
868             # this tend to be slow (?) but ensure correct output
869             # Note that cmdline may be long so I do not escape it
870             try:
871                 # since this is not an essential operation, proceed if things go wrong here.
872                 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
873             except:
874                 print "Warning: can not write to log file ", self.logfile
875         #
876         # if the command is not too long, use the old
877         if not self.longarg or len(cmdline) < 8000:
878             exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
879         else:
880             sAttrs = win32security.SECURITY_ATTRIBUTES()
881             StartupInfo = win32process.STARTUPINFO()
882             for var in spawnenv:
883                 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
884             # check for any special operating system commands
885             if cmd == 'del':
886                 for arg in args[1:]:
887                     win32file.DeleteFile(arg)
888                 exit_code = 0
889             else:
890                 # otherwise execute the command.
891                 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
892                 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
893                 exit_code = win32process.GetExitCodeProcess(hProcess)
894                 win32file.CloseHandle(hProcess);
895                 win32file.CloseHandle(hThread);
896         return exit_code
897
898
899 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
900     ''' This function modify env and allow logging of
901         commands to a logfile. If the argument is too long
902         a win32 spawn will be used instead of the system one
903     '''
904     #
905     # create a new spwn object
906     ls = loggedSpawn(env, logfile, longarg, info)
907     # replace the old SPAWN by the new function
908     env['SPAWN'] = ls.spawn
909