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