1 # vi:filetype=python:expandtab:tabstop=2:shiftwidth=2
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
9 # Full author contact details are available in file CREDITS.
11 # This file defines all the utility functions for the
12 # scons-based build system of lyx
15 import os, sys, re, shutil, glob
17 def writeToFile(filename, lines, append = False):
18 " utility function: write or append lines to filename "
20 file = open(filename, 'a')
22 file = open(filename, 'w')
27 def addToConfig(lines):
28 ''' utility function: shortcut for appending lines to outfile
29 add newline at the end of lines.
31 if lines.strip() != '':
32 writeToFile(os.path.join('src', 'config.h'),
33 lines + '\n\n', append = True)
36 def printEnvironment(env, keys=[]):
37 ''' used to check profile settings '''
38 dict = env.Dictionary()
44 # try to expand, but this is not always possible
45 print key, '=', env.subst('$'+key)
47 print '<<UNEXPANDED>>:', key, '=', dict[key]
50 def env_subst(target, source, env):
51 ''' subst variables in source by those in env, and output to target
52 source and target are scons File() objects
54 %key% (not key itself) is an indication of substitution
56 assert len(target) == 1
57 assert len(source) == 1
58 target_file = file(str(target[0]), "w")
59 source_file = file(str(source[0]), "r")
61 contents = source_file.read()
62 for (k, v) in env.items():
64 contents = re.sub('%'+k+'%', v, contents)
67 target_file.write(contents + "\n")
69 #st = os.stat(str(source[0]))
70 #os.chmod(str(target[0]), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
73 def env_filecopy(target, source, env):
74 shutil.copy(str(source[0]), str(target[0]))
81 def checkPkgConfig(conf, version):
82 ''' Return false if pkg_config does not exist, or is too old '''
83 conf.Message('Checking for pkg-config...')
84 ret = conf.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
89 def checkPackage(conf, pkg):
90 ''' check if pkg is under the control of conf '''
91 conf.Message('Checking for package %s...' % pkg)
92 ret = conf.TryAction("pkg-config --print-errors --exists %s" % pkg)[0]
97 def startConfigH(config_h):
98 ''' Write the first part of config.h '''
100 '''/* src/config.h. Generated by scon. */
105 * This file is part of LyX, the document processor.
106 * Licence details can be found in the file COPYING.
108 * This is the compilation configuration file for LyX.
109 * It was generated by scon.
110 * You might want to change some of the defaults if something goes wrong
111 * during the compilation.
119 def endConfigH(config_h):
120 ''' Write the last part of config.h '''
121 writeToFile(config_h, '''
122 /************************************************************
123 ** You should not need to change anything beyond this point */
125 #ifndef HAVE_STRERROR
126 #if defined(__cplusplus)
129 char * strerror(int n);
133 #ifndef HAVE_DECL_MKSTEMP
134 #if defined(__cplusplus)
142 # include "support/os2_defines.h"
145 #if defined(HAVE_OSTREAM) && defined(HAVE_LOCALE) && defined(HAVE_SSTREAM)
146 # define USE_BOOST_FORMAT 1
148 # define USE_BOOST_FORMAT 0
151 #define BOOST_USER_CONFIG <config.h>
153 #if !defined(ENABLE_ASSERTIONS)
154 # define BOOST_DISABLE_ASSERTS 1
156 #define BOOST_ENABLE_ASSERT_HANDLER 1
158 #define BOOST_DISABLE_THREADS 1
159 #define BOOST_NO_WREGEX 1
160 #define BOOST_NO_WSTRING 1
163 # define BOOST_POSIX 1
166 #if defined(HAVE_NEWAPIS_H)
167 # define WANT_GETFILEATTRIBUTESEX_WRAPPER 1
175 def checkPutenv(conf):
176 check_putenv_source = """
184 conf.Message('Checking for putenv... ')
185 ret = conf.TryLink(check_putenv_source, '.c')
190 #HAVE_DECL_ISTREAMBUF_ITERATOR
191 def checkIstreambufIterator(conf):
192 check_istreambuf_iterator_source = """
197 std::istreambuf_iterator<std::istream> iter;
201 conf.Message('Checking for iostreambuf::iterator... ')
202 ret = conf.TryLink(check_istreambuf_iterator_source, '.cpp')
208 def checkMkdirOneArg(conf):
209 check_mkdir_one_arg_source = """
210 #include <sys/stat.h>
216 conf.Message('Checking for the number of args for mkdir... ')
217 ret = conf.TryLink(check_mkdir_one_arg_source, '.c')
226 def checkStdCount(conf):
227 check_std_count_source = """
230 int countChar(char * b, char * e, char const c)
232 return count(b, e, c);
238 int i = countChar(a, a + 5, 'l');
241 conf.Message('Checking for std::count... ')
242 ret = conf.TryLink(check_std_count_source, '.cpp')
250 def checkSelectArgType(conf):
251 ''' Adapted from autoconf '''
252 conf.Message('Checking for arg types for select... ')
253 for arg234 in ['fd_set *', 'int *', 'void *']:
254 for arg1 in ['int', 'size_t', 'unsigned long', 'unsigned']:
255 for arg5 in ['struct timeval *', 'const struct timeval *']:
256 check_select_source = '''
257 #if HAVE_SYS_SELECT_H
258 # include <sys/select.h>
260 #if HAVE_SYS_SOCKET_H
261 # include <sys/socket.h>
263 extern int select (%s, %s, %s, %s, %s);
268 ''' % (arg1, arg234, arg234, arg234, arg5)
269 ret = conf.TryLink(check_select_source, '.c')
272 return (arg1, arg234, arg5)
273 conf.Result('no (use default)')
274 return ('int', 'int *', 'struct timeval *')
277 def checkBoostLibraries(conf, lib, pathes):
278 ''' look for boost libraries '''
279 conf.Message('Checking for boost library %s... ' % lib)
281 # direct form: e.g. libboost_iostreams.a
282 if os.path.isfile(os.path.join(path, 'lib%s.a' % lib)):
285 # check things like libboost_iostreams-gcc.a
286 files = glob.glob(os.path.join(path, 'lib%s-*.a' % lib))
287 # if there are more than one, choose the first one
288 # FIXME: choose the best one.
290 # get xxx-gcc from /usr/local/lib/libboost_xxx-gcc.a
292 return (path, files[0].split(os.sep)[-1][3:-2])
298 def processLang(env, folder):
299 """ Process translations (.po files) in a po/ dir
300 This is copied from KDE knetstats-1.5/admin/kde.py
305 dir=SCons.Node.FS.default_fs.Dir(folder).srcnode()
307 tmptransfiles = glob.glob(str(fld)+'/*.po')
310 if env.has_key('_BUILDDIR_'):
311 bdir=env['_BUILDDIR_']
312 for dir in env.make_list(tmptransfiles):
313 transfiles.append( env.join(bdir, dir) )
315 transfiles=tmptransfiles
317 env['MSGFMT'] = 'msgfmt'
318 env['BUILDERS']['Transfiles']=SCons.Builder.Builder(action='$MSGFMT $SOURCE -o $TARGET',suffix='.gmo',src_suffix='.po')
320 # FIXME: KDE has this ARGS thing...
321 #if env['ARGS'] and env['ARGS'].has_key('languages'):
322 # languages=env.make_list(env['ARGS']['languages'])
323 mydir=SCons.Node.FS.default_fs.Dir('.')
325 fname=f.replace(mydir.abspath, '')
326 file=SCons.Node.FS.default_fs.File(fname)
327 country = SCons.Util.splitext(file.name)[0]
328 if not languages or country in languages:
329 result = env.Transfiles(file)
331 # dir=env.join( getInstDirForResType(env, 'KDELOCALE'), country)
332 # env.bksys_install(env.join(dir, 'LC_MESSAGES'), result, destfile=appname+'.mo')
335 def installCygwinLDScript(path):
336 ''' Install i386pe.x-no-rdata '''
337 ld_script = os.path.join(path, 'i386pe.x-no-rdata')
338 script = open(ld_script, 'w')
339 script.write('''/* specific linker script avoiding .rdata sections, for normal executables
341 http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
342 http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
344 OUTPUT_FORMAT(pei-i386)
345 SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api");
346 ENTRY(_mainCRTStartup)
349 .text __image_base__ + __section_alignment__ :
356 ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
357 LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
358 ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
359 LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
361 /* ??? Why is .gcc_exc here? */
366 /* The Cygwin32 library uses a section to avoid copying certain data
367 on fork. This used to be named ".data". The linker used
368 to include this between __data_start__ and __data_end__, but that
369 breaks building the cygwin32 dll. Instead, we name the section
370 ".data_cygwin_nocopy" and explictly include it after __data_end__. */
371 .data BLOCK(__section_alignment__) :
380 ___RUNTIME_PSEUDO_RELOC_LIST__ = .;
381 __RUNTIME_PSEUDO_RELOC_LIST__ = .;
382 *(.rdata_runtime_pseudo_reloc)
383 ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
384 __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
386 *(.data_cygwin_nocopy)
388 .rdata BLOCK(__section_alignment__) :
391 .pdata BLOCK(__section_alignment__) :
395 .bss BLOCK(__section_alignment__) :
402 .edata BLOCK(__section_alignment__) :
413 .idata BLOCK(__section_alignment__) :
415 /* This cannot currently be handled with grouped sections.
416 See pe.em:sort_sections. */
419 /* These zeroes mark the end of the import list. */
420 LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
426 .CRT BLOCK(__section_alignment__) :
428 ___crt_xc_start__ = . ;
429 *(SORT(.CRT$XC*)) /* C initialization */
430 ___crt_xc_end__ = . ;
431 ___crt_xi_start__ = . ;
432 *(SORT(.CRT$XI*)) /* C++ initialization */
433 ___crt_xi_end__ = . ;
434 ___crt_xl_start__ = . ;
435 *(SORT(.CRT$XL*)) /* TLS callbacks */
436 /* ___crt_xl_end__ is defined in the TLS Directory support code */
437 ___crt_xp_start__ = . ;
438 *(SORT(.CRT$XP*)) /* Pre-termination */
439 ___crt_xp_end__ = . ;
440 ___crt_xt_start__ = . ;
441 *(SORT(.CRT$XT*)) /* Termination */
442 ___crt_xt_end__ = . ;
444 .tls BLOCK(__section_alignment__) :
452 .endjunk BLOCK(__section_alignment__) :
454 /* end is deprecated, don't use it */
459 .rsrc BLOCK(__section_alignment__) :
464 .reloc BLOCK(__section_alignment__) :
468 .stab BLOCK(__section_alignment__) (NOLOAD) :
472 .stabstr BLOCK(__section_alignment__) (NOLOAD) :
476 /* DWARF debug sections.
477 Symbols in the DWARF debugging sections are relative to the beginning
478 of the section. Unlike other targets that fake this by putting the
479 section VMA at 0, the PE format will not allow it. */
480 /* DWARF 1.1 and DWARF 2. */
481 .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
485 .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
490 .debug_info BLOCK(__section_alignment__) (NOLOAD) :
492 *(.debug_info) *(.gnu.linkonce.wi.*)
494 .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
498 .debug_line BLOCK(__section_alignment__) (NOLOAD) :
502 .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
506 .debug_str BLOCK(__section_alignment__) (NOLOAD) :
510 .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
514 .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
518 /* SGI/MIPS DWARF 2 extensions. */
519 .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
523 .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
527 .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
531 .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
536 .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
547 def __init__(self, env, logfile, longarg, info):
548 # save the spawn system
550 self.logfile = logfile
551 # clear the logfile (it may not exist)
553 # this will overwrite existing content.
554 writeToFile(logfile, info, append=False)
556 self.longarg = longarg
557 # get hold of the old spawn? (necessary?)
558 self._spawn = env['SPAWN']
561 def spawn(self, sh, escape, cmd, args, spawnenv):
563 newargs = ' '.join(map(escape, args[1:]))
564 cmdline = cmd + " " + newargs
566 # if log is not empty, write to it
567 if self.logfile != '':
568 # this tend to be slow (?) but ensure correct output
569 # Note that cmdline may be long so I do not escape it
571 # since this is not an essential operation, proceed if things go wrong here.
572 writeToFile(self.logfile, cmd + " " + ' '.join(args[1:]) + '\n', append=True)
574 print "Warning: can not write to log file ", self.logfile
576 # if the command is not too long, use the old
577 if not self.longarg or len(cmdline) < 8000:
578 exit_code = self._spawn(sh, escape, cmd, args, spawnenv)
580 sAttrs = win32security.SECURITY_ATTRIBUTES()
581 StartupInfo = win32process.STARTUPINFO()
583 spawnenv[var] = spawnenv[var].encode('ascii', 'replace')
584 # check for any special operating system commands
587 win32file.DeleteFile(arg)
590 # otherwise execute the command.
591 hProcess, hThread, dwPid, dwTid = win32process.CreateProcess(None, cmdline, None, None, 1, 0, spawnenv, None, StartupInfo)
592 win32event.WaitForSingleObject(hProcess, win32event.INFINITE)
593 exit_code = win32process.GetExitCodeProcess(hProcess)
594 win32file.CloseHandle(hProcess);
595 win32file.CloseHandle(hThread);
599 def setLoggedSpawn(env, logfile = '', longarg=False, info=''):
600 ''' This function modify env and allow logging of
601 commands to a logfile. If the argument is too long
602 a win32 spawn will be used instead of the system one
611 # create a new spwn object
612 ls = loggedSpawn(env, logfile, longarg, info)
613 # replace the old SPAWN by the new function
614 env['SPAWN'] = ls.spawn
617 ## def CheckPython(context, minver):
618 ## context.Message('Checking for Python >= %s...' % '.'.join(str(x) for x in minver))
620 ## python = context.env['PYTHON']
623 ## python = os.environ['PYTHON']
625 ## python = WhereIs("python")
627 ## python = python = WhereIs("python%i.%i" % (minver[0], minver[1]))
629 ## minver = list(minver) + [0, 0, 0, 0]
630 ## for i in xrange(0, 4):
631 ## minverhex = (minverhex << 8) + minver[i]
632 ## prog = "import sys; sys.exit(sys.hexversion >= %s)" % minverhex
633 ## if python is None:
636 ## result = Popen([python, "-c", prog]).wait()
638 ## context.Result(False)
640 ## context.Result(result)
642 ## context.env.Replace(PYTHON=python)
643 ## proc = Popen([python, "-c", "import sys; print sys.version[:3]"], stdout=PIPE)
644 ## pyver = proc.communicate()[0].rstrip()
645 ## context.env.Replace(PYTHON_VERSION=pyver)
646 ## context.env.Replace(pythondir="$prefix/lib/python$PYTHON_VERSION/site-packages")
647 ## context.env.Replace(pyexecdir="${exec_prefix}/lib/python$PYTHON_VERSION/site-packages")
650 ## def DistSources(env, node):
651 ## env.DistFiles(_get_sources(env, node))
653 ## def DistFiles(env, files):
654 ## assert isinstance(files, (list, tuple))
655 ## DISTFILES = [env.File(fname) for fname in files]
656 ## env.AppendUnique(DISTFILES=DISTFILES)
659 ## def make_distdir(target=None, source=None, env=None):
660 ## distdir = env.subst('$DISTDIR')
661 ## Execute(Delete(distdir))
662 ## Execute(Mkdir(distdir))
663 ## for fnode in env["DISTFILES"]:
664 ## dirname, fname = os.path.split(str(fnode))
666 ## distdirname = os.path.join(distdir, dirname)
667 ## if not os.path.exists(distdirname):
668 ## Execute(Mkdir(distdirname))
669 ## Execute(Copy(os.path.join(distdir, dirname, fname), str(fnode)))
671 ## def make_dist(target=None, source=None, env=None):
672 ## return Popen([env['TAR'], "-zcf",
673 ## env.subst("${PACKAGE}-${VERSION}.tar.gz"),
674 ## env.subst('$DISTDIR')]).wait()
676 ## def make_distcheck(target=None, source=None, env=None):
677 ## distdir = env.subst('$DISTDIR')
678 ## distcheckinstdir = tempfile.mkdtemp('', env.subst('${PACKAGE}-${VERSION}-instdir-'))
679 ## distcheckdestdir = tempfile.mkdtemp('', env.subst('${PACKAGE}-${VERSION}-destdir-'))
680 ## instdirs = [os.path.join(distcheckinstdir, d) for d in
681 ## 'lib', 'share', 'bin', 'include']
682 ## for dir_ in instdirs:
683 ## Execute(Mkdir(dir_))
685 ## cmd = env.subst("cd $DISTDIR && scons DESTDIR=%s prefix=%s"
686 ## " && scons check && scons install") %\
687 ## (os.path.join(distcheckdestdir, ''), distcheckinstdir)
688 ## status = Popen(cmd, shell=True).wait()
691 ## ## Check that inst dirs are empty (to catch cases of $DESTDIR not being honored
692 ## for dir_ in instdirs:
693 ## if os.listdir(dir_):
694 ## raise SCons.Errors.BuildError(target, "%s not empy" % dir_)
695 ## ## Check that something inside $DESTDIR was installed
696 ## dir_ = os.path.join(distcheckdestdir, distcheckinstdir)
697 ## if not os.path.exists(dir_):
698 ## raise SCons.Errors.BuildError(target, "%s does not exist" % dir_)
699 ## Execute(Delete(distcheckinstdir))
700 ## Execute(Delete(distcheckdestdir))
701 ## Execute(Delete(distdir))
703 ## def InstallWithDestDir(self, dir_, source):
704 ## dir_ = '${DESTDIR}' + str(dir_)
705 ## return SConsEnvironment.Install(self, dir_, source)
708 ## def InstallAsWithDestDir(self, target, source):
709 ## target = '${DESTDIR}' + str(target)
710 ## return SConsEnvironment.InstallAs(self, target, source)
712 ## def generate(env):
713 ## env.EnsureSConsVersion(0, 96, 91)
715 ## opts = Options(['options.cache'], ARGUMENTS)
716 ## opts.Add(PathOption('prefix', 'Installation prefix', '/usr/local'))
717 ## opts.Add(PathOption('exec_prefix', 'Installation prefix blah blah',
719 ## opts.Add(PathOption('libdir',
720 ## 'Installation prefix for architecture dependent files', '$prefix/lib'))
721 ## opts.Add(PathOption('includedir',
722 ## 'Installation prefix for C header files', '$prefix/include'))
723 ## opts.Add(PathOption('datadir',
724 ## 'Installation prefix for architecture independent files', '$prefix/share'))
725 ## opts.Add(PathOption('bindir', 'Installation prefix for programs', '$prefix/bin'))
726 ## opts.Add(PathOption('DESTDIR', 'blah blah', None))
728 ## opts.Save('options.cache', env)
729 ## SConsEnvironment.Help(env, opts.GenerateHelpText(env))
731 ## env.Append(CPPFLAGS=r' -DVERSION=\"$VERSION\"')
732 ## env.Append(CCFLAGS=ARGUMENTS.get('CCFLAGS', '-g -O2'))
734 ## env['GNOME_TESTS'] = dict(CheckPython=CheckPython,
735 ## CheckPythonHeaders=CheckPythonHeaders,
736 ## PkgCheckModules=PkgCheckModules)
738 ## SConsEnvironment.DistSources = DistSources
739 ## SConsEnvironment.DistFiles = DistFiles
740 ## env['DISTDIR'] = "${PACKAGE}-${VERSION}"
742 ## #env.Command(env.Dir("$DISTDIR"), None, make_distdir)
744 ## distdir_alias = env.Alias("distdir", None, make_distdir)
745 ## dist_alias = env.Alias("dist", None, make_dist)
746 ## env.Depends(dist_alias, distdir_alias)
747 ## distcheck_alias = env.Alias("distcheck", None, make_distcheck)
748 ## env.Depends(distcheck_alias, distdir_alias)
749 ## env.AlwaysBuild(env.Alias('check'))
751 ## #env['TARFLAGS'] ='-c -z'
752 ## #env['TARSUFFIX'] = '.tar.gz'
753 ## #tar = env.Tar('${PACKAGE}-${VERSION}.tar.gz', "${DISTDIR}")
754 ## #env.Depends(tar, distdir_alias)
755 ## #print env['DEFAULT_TARGETS']
757 ## #env.Depends(distdir_alias, "${DISTFILES}")
758 ## #env.Alias('dist', tar)
759 ## env.AlwaysBuild('dist')
760 ## env.AlwaysBuild('distdir')
761 ## env.AlwaysBuild('distcheck')
762 ## env.DistFiles(['SConstruct', 'scons/gnome.py'])
764 ## env['BUILDERS']['EnvSubstFile'] = SCons.Builder.Builder(action=env_subst)
766 ## SConsEnvironment.PythonByteCompile = env.Action(byte_compile_python)
768 ## env.Install = new.instancemethod(InstallWithDestDir, env, env.__class__)
769 ## env.InstallAs = new.instancemethod(InstallAsWithDestDir, env, env.__class__)