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