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