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