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