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