]> git.lyx.org Git - lyx.git/blob - development/scons/scons_utils.py
Chinese translation of the LyX tutorial, contributed by mgc.
[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 int i[ ( sizeof(wchar_t)==%d ? 1 : -1 ) ];
444 int main()
445 {
446     return 0;
447 }
448 '''
449     conf.Message('Checking the size of wchar_t... ')
450     if conf.TryLink(check_sizeof_wchar % 2, '.cpp'):
451         ret = 2
452     elif conf.TryLink(check_sizeof_wchar % 4, '.cpp'):
453         ret = 4
454     else:
455         ret = 0
456     conf.Result(str(ret))
457     return ret
458
459
460 def checkDeclaration(conf, func, headers):
461     ''' check if a function is declared in given headers '''
462     check_decl = '''
463 #include <%%s>
464 int main()
465 {
466 #ifndef %s
467     char *p = (char *) %s;
468 #endif
469 }
470 ''' % (func, func)
471     conf.Message('Checking for the declaration of function %s... ' % func)
472     ret = True in [conf.TryLink(check_decl % header, '.c') for header in headers]
473     conf.Result(ret)
474     return ret
475
476     
477 def createConfigFile(conf, config_file,
478     config_pre = '', config_post = '',
479     headers = [], functions = [], declarations = [], types = [], libs = [],
480     custom_tests = [], extra_items = []):
481     ''' create a configuration file, with options
482         config_file: which file to create
483         config_pre: first part of the config file
484         config_post: last part of the config file
485         headers: header files to check, in the form of a list of
486             ('file', 'HAVE_FILE', 'c'/'c++')
487         functions: functions to check, in the form of a list of
488             ('func', 'HAVE_func', 'include lines'/None)
489         declarations: function declarations to check, in the form of a list of
490             ('func', 'HAVE_DECL_func', header_files)
491         types: types to check, in the form of a list of
492             ('type', 'HAVE_TYPE', 'includelines'/None)
493         libs: libraries to check, in the form of a list of
494             ('lib', 'HAVE_LIB', 'LIB_NAME'). HAVE_LIB will be set if 'lib' exists,
495             or any of the libs exists if 'lib' is a list of libs.
496             Optionally, user can provide another key LIB_NAME, that will
497             be set to the detected lib (or None otherwise).
498         custom_tests: extra tests to perform, in the form of a list of
499             (test (True/False), 'key', 'desc', 'true config line', 'false config line')
500             If the last two are ignored, '#define key 1' '/*#undef key */'
501             will be used.
502         extra_items: extra configuration lines, in the form of a list of
503             ('config', 'description')
504     Return:
505         The result of each test, as a dictioanry of
506             res['XXX'] = True/False
507         XXX are keys defined in each argument.
508     '''
509     cont = config_pre + '\n'
510     result = {}
511     # add to this string, in appropriate format
512     def configString(lines, desc=''):
513         text = ''
514         if lines.strip() != '':
515             if desc != '':
516                 text += '/* ' + desc + ' */\n'
517             text += lines + '\n\n'
518         return text
519     #
520     # headers
521     for header in headers:
522         description = "Define to 1 if you have the <%s> header file." % header[0]
523         if (header[2] == 'c' and conf.CheckCHeader(header[0])) or \
524             (header[2] == 'cxx' and conf.CheckCXXHeader(header[0])):
525             result[header[1]] = 1
526             cont += configString('#define %s 1' % header[1], desc = description)
527         else:
528             result[header[1]] = 0
529             cont += configString('/* #undef %s */' % header[1], desc = description)
530     # functions
531     for func in functions:
532         description = "Define to 1 if you have the `%s' function." % func[0]
533         if conf.CheckFunc(func[0], header=func[2]):
534             result[func[1]] = 1
535             cont += configString('#define %s 1' % func[1], desc = description)
536         else:
537             result[func[1]] = 0
538             cont += configString('/* #undef %s */' % func[1], desc = description)
539     for decl in declarations:
540         description = "Define to 1 if you have the declaration of `%s', and to 0 if you don't." % decl[0]
541         if conf.CheckDeclaration(decl[0], decl[2]):
542             result[decl[1]] = 1
543             cont += configString('#define %s 1' % decl[1], desc = description)
544         else:
545             result[decl[1]] = 0
546             cont += configString('/* #undef %s */' % decl[1], desc = description)
547     # types
548     for t in types:
549         description = "Define to 1 if you have the `%s' type." % t[0]
550         if conf.CheckType(t[0], includes=t[2]):
551             result[t[1]] = 1
552             cont += configString('#define %s 1' % t[1], desc = description)
553         else:
554             result[t[1]] = 0
555             cont += configString('/* #undef %s */' % t[1],  desc = description)
556     # libraries
557     for lib in libs:
558         description = "Define to 1 if you have the `%s' library (-l%s)." % (lib[0], lib[0])
559         if type(lib[0]) is type(''):
560             lib_list = [lib[0]]
561         else:
562             lib_list = lib[0]
563         # check if any of the lib exists
564         result[lib[1]] = 0
565         # if user want the name of the lib detected
566         if len(lib) == 3:
567             result[lib[2]] = None
568         for ll in lib_list:
569             if conf.CheckLib(ll):
570                 result[lib[1]] = 1
571                 if len(lib) == 3:
572                     result[lib[2]] = ll
573                 cont += configString('#define %s 1' % lib[1], desc = description)
574                 break
575         # if not found
576         if not result[lib[1]]:
577             cont += configString('/* #undef %s */' % lib[1], desc = description)
578     # custom tests
579     for test in custom_tests:
580         if test[0]:
581             result[test[1]] = 1
582             if len(test) == 3:
583                 cont += configString('#define %s 1' % test[1], desc = test[2])
584             else:
585                 cont += configString(test[3], desc = test[2])
586         else:
587             result[test[1]] = 0
588             if len(test) == 3:
589                 cont += configString('/* #undef %s */' % test[1], desc = test[2])
590             else:
591                 cont += configString(test[4], desc = test[2])
592     # extra items (no key is returned)
593     for item in extra_items:
594         cont += configString(item[0], desc = item[1])
595     # add the last part
596     cont += '\n' + config_post + '\n'
597     # write to file
598     writeToFile(config_file, cont)
599     return result
600
601
602 def installCygwinLDScript(path):
603     ''' Install i386pe.x-no-rdata '''
604     ld_script = os.path.join(path, 'i386pe.x-no-rdata')
605     script = open(ld_script, 'w')
606     script.write('''/* specific linker script avoiding .rdata sections, for normal executables
607 for a reference see
608 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
609 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
610 */
611 OUTPUT_FORMAT(pei-i386)
612 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
613 ENTRY(_mainCRTStartup)
614 SECTIONS
615 {
616   .text  __image_base__ + __section_alignment__  :
617   {
618     *(.init)
619     *(.text)
620     *(SORT(.text$*))
621     *(.glue_7t)
622     *(.glue_7)
623     ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
624                         LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*));  LONG (0);
625     ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
626                         LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*));  LONG (0);
627     *(.fini)
628     /* ??? Why is .gcc_exc here?  */
629     *(.gcc_exc)
630     PROVIDE (etext = .);
631     *(.gcc_except_table)
632   }
633   /* The Cygwin32 library uses a section to avoid copying certain data
634     on fork.  This used to be named ".data".  The linker used
635     to include this between __data_start__ and __data_end__, but that
636     breaks building the cygwin32 dll.  Instead, we name the section
637     ".data_cygwin_nocopy" and explictly include it after __data_end__. */
638   .data BLOCK(__section_alignment__) :
639   {
640     __data_start__ = . ;
641     *(.data)
642     *(.data2)
643     *(SORT(.data$*))
644     *(.rdata)
645     *(SORT(.rdata$*))
646     *(.eh_frame)
647     ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
648     __RUNTIME_PSEUDO_RELOC_LIST__ = .;
649     *(.rdata_runtime_pseudo_reloc)
650     ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
651     __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
652     __data_end__ = . ;
653     *(.data_cygwin_nocopy)
654   }
655   .rdata BLOCK(__section_alignment__) :
656   {
657   }
658   .pdata BLOCK(__section_alignment__) :
659   {
660     *(.pdata)
661   }
662   .bss BLOCK(__section_alignment__) :
663   {
664     __bss_start__ = . ;
665     *(.bss)
666     *(COMMON)
667     __bss_end__ = . ;
668   }
669   .edata BLOCK(__section_alignment__) :
670   {
671     *(.edata)
672   }
673   /DISCARD/ :
674   {
675     *(.debug$S)
676     *(.debug$T)
677     *(.debug$F)
678     *(.drectve)
679   }
680   .idata BLOCK(__section_alignment__) :
681   {
682     /* This cannot currently be handled with grouped sections.
683         See pe.em:sort_sections.  */
684     SORT(*)(.idata$2)
685     SORT(*)(.idata$3)
686     /* These zeroes mark the end of the import list.  */
687     LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
688     SORT(*)(.idata$4)
689     SORT(*)(.idata$5)
690     SORT(*)(.idata$6)
691     SORT(*)(.idata$7)
692   }
693   .CRT BLOCK(__section_alignment__) :
694   {
695     ___crt_xc_start__ = . ;
696     *(SORT(.CRT$XC*))  /* C initialization */
697     ___crt_xc_end__ = . ;
698     ___crt_xi_start__ = . ;
699     *(SORT(.CRT$XI*))  /* C++ initialization */
700     ___crt_xi_end__ = . ;
701     ___crt_xl_start__ = . ;
702     *(SORT(.CRT$XL*))  /* TLS callbacks */
703     /* ___crt_xl_end__ is defined in the TLS Directory support code */
704     ___crt_xp_start__ = . ;
705     *(SORT(.CRT$XP*))  /* Pre-termination */
706     ___crt_xp_end__ = . ;
707     ___crt_xt_start__ = . ;
708     *(SORT(.CRT$XT*))  /* Termination */
709     ___crt_xt_end__ = . ;
710   }
711   .tls BLOCK(__section_alignment__) :
712   {
713     ___tls_start__ = . ;
714     *(.tls)
715     *(.tls$)
716     *(SORT(.tls$*))
717     ___tls_end__ = . ;
718   }
719   .endjunk BLOCK(__section_alignment__) :
720   {
721     /* end is deprecated, don't use it */
722     PROVIDE (end = .);
723     PROVIDE ( _end = .);
724     __end__ = .;
725   }
726   .rsrc BLOCK(__section_alignment__) :
727   {
728     *(.rsrc)
729     *(SORT(.rsrc$*))
730   }
731   .reloc BLOCK(__section_alignment__) :
732   {
733     *(.reloc)
734   }
735   .stab BLOCK(__section_alignment__) (NOLOAD) :
736   {
737     *(.stab)
738   }
739   .stabstr BLOCK(__section_alignment__) (NOLOAD) :
740   {
741     *(.stabstr)
742   }
743   /* DWARF debug sections.
744     Symbols in the DWARF debugging sections are relative to the beginning
745     of the section.  Unlike other targets that fake this by putting the
746     section VMA at 0, the PE format will not allow it.  */
747   /* DWARF 1.1 and DWARF 2.  */
748   .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
749   {
750     *(.debug_aranges)
751   }
752   .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
753   {
754     *(.debug_pubnames)
755   }
756   /* DWARF 2.  */
757   .debug_info BLOCK(__section_alignment__) (NOLOAD) :
758   {
759     *(.debug_info) *(.gnu.linkonce.wi.*)
760   }
761   .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
762   {
763     *(.debug_abbrev)
764   }
765   .debug_line BLOCK(__section_alignment__) (NOLOAD) :
766   {
767     *(.debug_line)
768   }
769   .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
770   {
771     *(.debug_frame)
772   }
773   .debug_str BLOCK(__section_alignment__) (NOLOAD) :
774   {
775     *(.debug_str)
776   }
777   .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
778   {
779     *(.debug_loc)
780   }
781   .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
782   {
783     *(.debug_macinfo)
784   }
785   /* SGI/MIPS DWARF 2 extensions.  */
786   .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
787   {
788     *(.debug_weaknames)
789   }
790   .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
791   {
792     *(.debug_funcnames)
793   }
794   .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
795   {
796     *(.debug_typenames)
797   }
798   .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
799   {
800     *(.debug_varnames)
801   }
802   /* DWARF 3.  */
803   .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
804   {
805     *(.debug_ranges)
806   }
807 }
808 ''')
809     script.close()
810     return(ld_script)
811
812
813 def installCygwinPostinstallScript(path):
814     ''' Install lyx.sh '''
815     postinstall_script = os.path.join(path, 'lyx.sh')
816     script = open(postinstall_script, 'w')
817     script.write(r'''#!/bin/sh
818
819 # Add /usr/share/lyx/fonts to /etc/fonts/local.conf
820 # if it is not already there.
821 if [ -f /etc/fonts/local.conf ]; then
822     grep -q /usr/share/lyx/fonts /etc/fonts/local.conf
823     if [ $? -ne 0 ]; then
824         sed 's/^<\/fontconfig>/<dir>\/usr\/share\/lyx\/fonts<\/dir>\n<\/fontconfig>/' /etc/fonts/local.conf > /etc/fonts/local.conf.tmp
825         mv -f /etc/fonts/local.conf.tmp /etc/fonts/local.conf
826         fc-cache /usr/share/lyx/fonts
827     fi
828 fi
829     ''')
830     script.close()
831     return(postinstall_script)
832
833
834 try:
835     # these will be used under win32
836     import win32file
837     import win32event
838     import win32process
839     import win32security
840 except:
841     # does not matter if it fails on other systems
842     pass
843
844
845 class loggedSpawn:
846     def __init__(self, env, logfile, longarg, info):
847         # save the spawn system
848         self.env = env
849         self.logfile = logfile
850         # clear the logfile (it may not exist)
851         if logfile != '':
852             # this will overwrite existing content.
853             writeToFile(logfile, info, append=False)
854         #
855         self.longarg = longarg
856         # get hold of the old spawn? (necessary?)
857         self._spawn = env['SPAWN']
858
859     # define new SPAWN
860     def spawn(self, sh, escape, cmd, args, spawnenv):
861         # get command line
862         newargs = ' '.join(map(escape, args[1:]))
863         cmdline = cmd + " " + newargs
864         #
865         # if log is not empty, write to it
866         if self.logfile != '':
867             # this tend to be slow (?) but ensure correct output
868             # Note that cmdline may be long so I do not escape it
869             try:
870                 # since this is not an essential operation, proceed if things go wrong here.
871                 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
872             except:
873                 print "Warning: can not write to log file ", self.logfile
874         #
875         # if the command is not too long, use the old
876         if not self.longarg or len(cmdline) < 8000:
877             exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
878         else:
879             sAttrs = win32security.SECURITY_ATTRIBUTES()
880             StartupInfo = win32process.STARTUPINFO()
881             for var in spawnenv:
882                 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
883             # check for any special operating system commands
884             if cmd == 'del':
885                 for arg in args[1:]:
886                     win32file.DeleteFile(arg)
887                 exit_code = 0
888             else:
889                 # otherwise execute the command.
890                 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
891                 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
892                 exit_code = win32process.GetExitCodeProcess(hProcess)
893                 win32file.CloseHandle(hProcess);
894                 win32file.CloseHandle(hThread);
895         return exit_code
896
897
898 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
899     ''' This function modify env and allow logging of
900         commands to a logfile. If the argument is too long
901         a win32 spawn will be used instead of the system one
902     '''
903     #
904     # create a new spwn object
905     ls = loggedSpawn(env, logfile, longarg, info)
906     # replace the old SPAWN by the new function
907     env['SPAWN'] = ls.spawn
908