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