]> git.lyx.org Git - lyx.git/blobdiff - development/scons/SConstruct
Scons: check minor version number (>=0.96.92)
[lyx.git] / development / scons / SConstruct
index 7a6ef25cd8bc1b61662353a9b06f0ad0d554d7ec..465f9cb9fe88b40336a422b1dd4acf7e585eb206 100644 (file)
@@ -24,8 +24,14 @@ import scons_utils as utils
 
 # scons asks for 1.5.2, lyx requires 2.3
 EnsurePythonVersion(2, 3)
-# Please use at least 0.96.91 (not 0.96.1)
+# Please use at least 0.96.92 (not 0.96.1)
 EnsureSConsVersion(0, 96)
+# also check for minor version number for scons 0.96
+from SCons import __version__
+version = map(int, __version__.split('.'))
+if version[0] == 0 and version[1] == 96 and version[2] < 92:
+    print "Scons >= 0.96.92 is required."
+    Exit(1)
 
 # determine where I am ...
 #
@@ -49,8 +55,10 @@ else:
 # only 1.4.x has frontends/qt2
 if os.path.isdir(os.path.join(top_src_dir, 'src', 'frontends', 'qt2')):
     package_version = '1.4.2svn'
+    boost_version = '1_32'
 else:
     package_version = '1.5.0svn'
+    boost_version = '1_33_1'
 
 devel_version = True
 default_build_mode = 'debug'
@@ -86,7 +94,7 @@ elif os.name == 'posix' and sys.platform != 'cygwin':
 elif os.name == 'posix' and sys.platform == 'cygwin':
     platform_name = 'cygwin'
     default_frontend = 'qt3'
-    default_prefix = '/usr/local'
+    default_prefix = '/usr'
     default_with_x = True
     default_packaging_method = 'posix'
 elif os.name == 'darwin':
@@ -120,7 +128,7 @@ opts = Options(['config.py'])
 opts.AddOptions(
     # frontend
     EnumOption('frontend', 'Main GUI', default_frontend,
-        allowed_values = ('xform', 'qt2', 'qt3', 'qt4', 'gtk') ),
+        allowed_values = ('qt2', 'qt3', 'qt4', 'gtk') ),
     # debug or release build
     EnumOption('mode', 'Building method', default_build_mode,
         allowed_values = ('debug', 'release') ),
@@ -174,8 +182,6 @@ opts.AddOptions(
     PathOption('qt_inc_path', 'Path to qt include directory', None),
     #
     PathOption('qt_lib_path', 'Path to qt library directory', None),
-    # build directory, will use $mode if not set
-    PathOption('build_dir', 'Build directory', None),
     # extra include and libpath
     PathOption('extra_inc_path', 'Extra include path', None),
     #
@@ -187,9 +193,14 @@ opts.AddOptions(
     #
     PathOption('extra_lib_path1', 'Extra library path', None),
     # rebuild only specifed, comma separated targets
-    ('rebuild', 'rebuild only specifed, comma separated targets', None),
+    ('rebuild', '''rebuild only specifed, comma separated targets.
+        yes or all (default): rebuild everything
+        no or none: rebuild nothing (usually used for installation)
+        comp1,comp2,...: rebuild specified targets''', None),
     # can be set to a non-existing directory
     ('prefix', 'install architecture-independent files in PREFIX', default_prefix),
+    # build directory, will use $mode if not set
+    ('build_dir', 'Build directory', None),
     # version suffix
     ('version_suffix', 'install lyx as lyx-suffix', None),
     # how to load options
@@ -218,6 +229,9 @@ opts.AddOptions(
     ('LINKFLAGS', 'replace default $LINKFLAGS', None),
 )
 
+# allowed options
+all_options = [x.key for x in opts.options]
+
 # copied from SCons/Options/BoolOption.py
 # We need to use them before a boolean ARGUMENTS option is available
 # in env as bool.
@@ -266,10 +280,15 @@ if (not ARGUMENTS.has_key('load_option') or \
                 print "  ** fast_start is disabled because of the change of option", arg
                 print
                 fast_start = False
-    # and we do not cache some options
-    for arg in ['fast_start', 'load_option']:
+    # and we do not cache some options (dest_dir is obsolete)
+    for arg in ['fast_start', 'load_option', 'dest_dir']:
         if opt_cache.has_key(arg):
             opt_cache.pop(arg)
+    # remove obsolete cached keys (well, SConstruct is evolving. :-)
+    for arg in opt_cache.keys():
+        if arg not in all_options:
+            print 'Option %s is obsolete, do not load it' % arg
+            opt_cache.pop(arg)
     # now, if load_option=opt1,opt2 or -opt1,opt2
     if ARGUMENTS.has_key('load_option') and \
         ARGUMENTS['load_option'] not in true_strings + false_strings:
@@ -291,6 +310,16 @@ if (not ARGUMENTS.has_key('load_option') or \
             print "Restoring cached option  %s=%s" % (key, ARGUMENTS[key])
     print
 
+# check if there is unused (or misspelled) argument
+for arg in ARGUMENTS.keys():
+    if arg not in all_options:
+        import textwrap
+        print "Unknown option '%s'... exiting." % arg
+        print
+        print "Available options are (check 'scons -help' for details):"
+        print '    ' + '\n    '.join(textwrap.wrap(',  '.join(all_options)))
+        Exit(1)
+
 # save arguments
 env_cache['arg_cache'] = ARGUMENTS
 
@@ -332,6 +361,15 @@ else:
 # to build multiple build_dirs using the same source
 # $mode can be debug or release
 if env.has_key('build_dir') and env['build_dir'] is not None:
+    # create the directory if needed
+    if not os.path.isdir(env['build_dir']):
+        try:
+            os.makedirs(env['build_dir'])
+        except:
+            pass
+        if not os.path.isdir(env['build_dir']):
+            print 'Can not create directory', env['build_dir']
+            Exit(3)
     env['BUILDDIR'] = env['build_dir']
 else:
     # Determine the name of the build $mode
@@ -355,10 +393,6 @@ env.AppendUnique(LIBPATH = ['$LOCALLIBPATH'])
 #       PACKAGE_VERSION
 #     src/version.C.in
 #       PACKAGE_VERSION, VERSION_INFO
-#     src/frontends/xforms/lyx_xpm.h.in
-#       XPM_H_LOCATION
-#     src/frontends/xforms/lyx_forms.h.in
-#       FORMS_H_LOCATION
 
 # full path name is used to build msvs project files
 # and to replace TOP_SRCDIR in package.C
@@ -541,20 +575,28 @@ if env.has_key('dest_dir'):
 
 if env.has_key('qt_dir') and env['qt_dir']:
     env['QTDIR'] = env['qt_dir']
-    # add path to the qt tools
-    env.AppendUnique(LIBPATH = [os.path.join(env['qt_dir'], 'lib')])
-    # set environment so that moc etc can be found even if its path is not set properly
-    env.PrependENVPath('PATH', os.path.join(env['qt_dir'], 'bin'))
 elif os.path.isdir(os.environ.get('QTDIR', '/usr/lib/qt-3.3')):
     env['QTDIR'] = os.environ.get('QTDIR', '/usr/lib/qt-3.3')
 
+# if there is a valid QTDIR, set path for lib and bin directories
+if env.has_key('QTDIR'):
+    # add path to the qt tools
+    if os.path.isdir(os.path.join(env['QTDIR'], 'lib')):
+        env.AppendUnique(LIBPATH = [os.path.join(env['QTDIR'], 'lib')])
+    # set environment so that moc etc can be found even if its path is not set properly
+    if os.path.isdir(os.path.join(env['QTDIR'], 'bin')):
+        os.environ['PATH'] += os.pathsep + os.path.join(env['QTDIR'], 'bin')
+        env.PrependENVPath('PATH', os.path.join(env['QTDIR'], 'bin'))
+
+# allow qt2 frontend to locate qt3 libs.
+frontend_lib = {'qt2':'qt3', 'qt3':'qt3', 'qt4':'qt4'}[frontend]
 if env.has_key('qt_lib_path') and env['qt_lib_path']:
     qt_lib_path = env.subst('$qt_lib_path')
 elif env.has_key('QTDIR') and os.path.isdir(os.path.join(env.subst('$QTDIR'), 'lib')):
     qt_lib_path = env.subst('$QTDIR/lib')
 # this is the path for cygwin.
-elif os.path.isdir(os.path.join('/usr/lib/', frontend, 'lib')):
-    qt_lib_path = env.subst('/usr/lib/$frontend/lib')
+elif os.path.isdir(os.path.join('/usr/lib/', frontend_lib, 'lib')):
+    qt_lib_path = '/usr/lib/%s/lib' % frontend_lib
 else:
     print "Qt library directory is not found. Please specify it using qt_lib_path"
     Exit(1)
@@ -567,8 +609,8 @@ if env.has_key('qt_inc_path') and env['qt_inc_path']:
 elif env.has_key('QTDIR') and os.path.isdir(os.path.join(env.subst('$QTDIR'), 'include')):
     qt_inc_path = '$QTDIR/include'
 # this is the path for cygwin.
-elif os.path.isdir('/usr/include/' + frontend):
-    qt_inc_path = '/usr/include/$frontend'
+elif os.path.isdir('/usr/include/' + frontend_lib):
+    qt_inc_path = '/usr/include/' + frontend_lib
 else:
     print "Qt include directory not found. Please specify it using qt_inc_path"
     Exit(1)
@@ -589,9 +631,9 @@ if env.has_key('extra_inc_path1') and env['extra_inc_path1']:
 if env.has_key('extra_lib_path1') and env['extra_lib_path1']:
     env.AppendUnique(LIBPATH = [env['extra_lib_path1']])
 if env.has_key('extra_bin_path') and env['extra_bin_path']:
-    # maybe only one of them is needed
+    # only the first one is needed (a scons bug?)
     os.environ['PATH'] += os.pathsep + env['extra_bin_path']
-    env['ENV']['PATH'] += os.pathsep + env['extra_bin_path']
+    env.PrependENVPath('PATH', env['extra_bin_path'])
 # extra_inc_paths will be used later by intlenv etc
 env.AppendUnique(CPPPATH = extra_inc_paths)
 
@@ -701,42 +743,43 @@ if not fast_start:
     # check boost libraries
     boost_opt = ARGUMENTS.get('boost', 'auto')
     # check for system boost
-    paths = env['LIBPATH'] + ['/usr/lib', '/usr/local/lib']
-    # real libraries (included or system)
-    boost_libraries = []
+    lib_paths = env['LIBPATH'] + ['/usr/lib', '/usr/local/lib']
+    inc_paths = env['CPPPATH'] + ['/usr/include', '/usr/local/include']
+    # default to $BUILDDIR/libs (use None since this path will be added anyway)
     boost_libpath = None
     # here I assume that all libraries are in the same directory
-    for lib in boost_libs:
-        if boost_opt == 'included':
-            boost_libraries.append('included_boost_%s' % lib)
-            env['INCLUDED_BOOST'] = True
-        elif boost_opt == 'auto':
-            res = conf.CheckBoostLibraries('boost_%s' % lib , paths)
-            # if not found
-            if res[0] == '':
-                boost_libraries.append('included_boost_%s' % lib)
-                env['INCLUDED_BOOST'] = True
-            else:
-                boost_libraries.append(res[1])
-                env['INCLUDED_BOOST'] = False
-                boost_libpath = res[0]
-        elif boost_opt == 'system':
-            res = conf.CheckBoostLibraries('boost_%s' % lib , paths)
-            if res[0] == '':
-                print "Can not find system boost libraries"
-                print "Please supply a path through extra_lib_path and try again."
-                print "Or use boost=included to use included boost libraries."
-                Exit(2)
-            else:
-                boost_libraries.append(res[1])
-                env.AppendUnique(LIBPATH = [res[0]])
-                boost_libpath = res[0]
+    if boost_opt == 'included':
+        boost_libraries = ['included_boost_%s' % x for x in boost_libs]
+        included_boost = True
+        env['BOOST_INC_PATH'] = '$TOP_SRCDIR/boost'
+    elif boost_opt == 'auto':
+        res = conf.CheckBoostLibraries(boost_libs, lib_paths, inc_paths, boost_version, mode == 'debug')
+        # if not found, use local boost
+        if res[0] is None:
+            boost_libraries = ['included_boost_%s' % x for x in boost_libs]
+            included_boost = True
+            env['BOOST_INC_PATH'] = '$TOP_SRCDIR/boost'
+        else:
+            included_boost = False
+            (boost_libraries, boost_libpath, env['BOOST_INC_PATH']) = res
+    elif boost_opt == 'system':
+        res = conf.CheckBoostLibraries(boost_libs, lib_paths, inc_paths, boost_version, mode == 'debug')
+        if res[0] is None:
+            print "Can not find system boost libraries with version %s " % boost_version
+            print "Please supply a path through extra_lib_path and try again."
+            print "Or use boost=included to use included boost libraries."
+            Exit(2)
+        else:
+            included_boost = False
+            (boost_libraries, boost_libpath, env['BOOST_INC_PATH']) = res
     env_cache['BOOST_LIBRARIES'] = boost_libraries
-    env_cache['INCLUDED_BOOST'] = env['INCLUDED_BOOST']
+    env_cache['INCLUDED_BOOST'] = included_boost
+    env_cache['BOOST_INC_PATH'] = env['BOOST_INC_PATH']
     env_cache['BOOST_LIBPATH'] = boost_libpath
 else:
     boost_libraries = env_cache['BOOST_LIBRARIES']
-    env['INCLUDED_BOOST'] = env_cache['INCLUDED_BOOST']
+    included_boost = env_cache['INCLUDED_BOOST']
+    env['BOOST_INC_PATH'] = env_cache['BOOST_INC_PATH']
     boost_libpath = env_cache['BOOST_LIBPATH']
 
 if boost_libpath is not None:
@@ -1307,7 +1350,20 @@ if env['X11']:
 # BUILDDIR/common: for config.h
 # TOP_SRCDIR/src: for support/* etc
 #
-env['CPPPATH'] += ['$TOP_SRCDIR/boost', '$BUILDDIR/common', '$TOP_SRCDIR/src']
+env['CPPPATH'] += ['$BUILDDIR/common', '$TOP_SRCDIR/src']
+#
+# Separating boost directories from CPPPATH stops scons from building
+# the dependency tree for boost header files, and effectively reduce
+# the null build time of lyx from 29s to 16s. Since lyx may tweak local
+# boost headers, this is only done for system boost headers.
+if included_boost:
+    env.AppendUnique(CPPPATH = ['$BOOST_INC_PATH'])
+else:
+    if use_vc:
+        env.PrependUnique(CCFLAGS = ['/I$BOOST_INC_PATH'])
+    else:
+        env.PrependUnique(CCFLAGS = ['-I$BOOST_INC_PATH'])
+
 # for intl/config.h, intl/libintl.h and intl/libgnuintl.h
 if env['nls'] and included_gettext:
     env['CPPPATH'].append('$BUILDDIR/intl')
@@ -1418,6 +1474,12 @@ print "Building all targets recursively"
 
 if env.has_key('rebuild'):
     rebuild_targets = env['rebuild'].split(',')
+    if 'none' in rebuild_targets or 'no' in rebuild_targets:
+        rebuild_targets = []
+    elif 'all' in rebuild_targets or 'yes' in rebuild_targets:
+        # None: let scons decide which components to build
+        # Forcing all components to be rebuilt is in theory not necessary
+        rebuild_targets = None    
 else:
     rebuild_targets = None
 
@@ -1425,11 +1487,15 @@ def libExists(libname):
     ''' Check whether or not lib $LOCALLIBNAME/libname already exists'''
     return os.path.isfile(File(env.subst('$LOCALLIBPATH/${LIBPREFIX}%s$LIBSUFFIX'%libname)).abspath)
 
+def appExists(apppath, appname):
+    ''' Check whether or not application already exists'''
+    return os.path.isfile(File(env.subst('$BUILDDIR/common/%s/${PROGPREFIX}%s$PROGSUFFIX' % (apppath, appname))).abspath)
+
 targets = BUILD_TARGETS
 # msvc need to pass full target name, so I have to look for path/lyx etc
 build_lyx = targets == [] or True in ['lyx' in x for x in targets] \
     or 'install' in targets or 'all' in targets
-build_boost = (env['INCLUDED_BOOST'] and not libExists('boost_regex')) or 'boost' in targets
+build_boost = (included_boost and not libExists('boost_regex')) or 'boost' in targets
 build_intl = (included_gettext and not libExists('included_intl')) or 'intl' in targets
 build_support = build_lyx or True in [x in targets for x in ['support', 'client', 'tex2lyx']]
 build_mathed = build_lyx or 'mathed' in targets
@@ -1450,8 +1516,8 @@ build_msvs_projects = use_vc and 'msvs_projects' in targets
 
 
 # now, if rebuild_targets is specified, do not rebuild some targets
-rebuild_targets = rebuild_targets
-if rebuild_targets:
+if rebuild_targets is not None:
+    #
     def ifBuildLib(name, libname, old_value):
         # explicitly asked to rebuild
         if name in rebuild_targets:
@@ -1474,6 +1540,19 @@ if rebuild_targets:
     build_qt2 = ifBuildLib('qt2', 'qt2', build_qt2)
     build_qt3 = ifBuildLib('qt3', 'qt3', build_qt3)
     build_qt4 = ifBuildLib('qt4', 'qt4', build_qt4)
+    #
+    def ifBuildApp(name, appname, old_value):
+        # explicitly asked to rebuild
+        if name in rebuild_targets:
+            return True
+        # else if not rebuild, and if the library already exists
+        elif appExists(name, appname):
+            return False
+        # do not change the original value
+        else:
+            return old_value
+    build_tex2lyx = ifBuildApp('tex2lyx', 'tex2lyx', build_tex2lyx)
+    build_client = ifBuildApp('client', 'lyxclient', build_client)
 
 # sync frontend and frontend (maybe build qt4 with frontend=qt3)
 if build_qt2:
@@ -1881,6 +1960,7 @@ if build_qt4:
         '-DQT_GENUINE_STR',
         '-DQT_NO_STL',
         '-DQT3_SUPPORT',
+        '-DQT_NO_KEYWORDS',
         ]
     )
 
@@ -1990,6 +2070,12 @@ if build_client:
     else:
         client = None
     Alias('client', client)
+else:
+    if env['HAVE_FCNTL']:
+        # define client even if lyxclient is not built with rebuild=no
+        client = [env.subst('$BUILDDIR/common/client/${PROGPREFIX}lyxclient$PROGSUFFIX')]
+    else:
+        client = None
 
 
 if build_tex2lyx:
@@ -2019,6 +2105,9 @@ if build_tex2lyx:
     Alias('tex2lyx', env.Command(os.path.join('$BUILDDIR', os.path.split(str(tex2lyx[0]))[1]),
         tex2lyx, [Copy('$TARGET', '$SOURCE')]))
     Alias('tex2lyx', tex2lyx)
+else:
+    # define tex2lyx even if tex2lyx is not built with rebuild=no
+    tex2lyx = [env.subst('$BUILDDIR/common/tex2lyx/${PROGPREFIX}tex2lyx$PROGSUFFIX')]
 
 
 if build_lyxbase:
@@ -2091,6 +2180,9 @@ if build_lyx:
     Alias('lyx', env.Command(os.path.join('$BUILDDIR', target_name), lyx,
         [Copy('$TARGET', '$SOURCE')]))
     Alias('lyx', lyx)
+else:
+    # define lyx even if lyx is not built with rebuild=no
+    lyx = [env.subst('$BUILDDIR/$frontend/${PROGPREFIX}lyx$PROGSUFFIX')]
 
 
 if build_msvs_projects:
@@ -2115,9 +2207,9 @@ if build_msvs_projects:
         else:
             res = []
         if rebuildTargetOnly:
-            cmds = 'faststart=yes rebuild='+target
+            cmds = 'fast_start=yes rebuild='+target
         else:
-            cmds = 'faststart=yes'
+            cmds = 'fast_start=yes'
         if type(dir) == type([]):
             src = []
             inc = []
@@ -2211,13 +2303,13 @@ if build_po:
     if env.has_key('languages'):
         languages = env.make_list(env['lanauges'])
     # use defulat msgfmt
+    gmo_files = []
     if not env['MSGFMT']:
         print 'msgfmt does not exist. Can not process po files'
     else:
         # create a builder
         env['BUILDERS']['Transfiles'] = Builder(action='$MSGFMT $SOURCE -o $TARGET',suffix='.gmo',src_suffix='.po')
         #
-        gmo_files = []
         for f in transfiles:
             # get filename
             fname = os.path.split(f)[1]
@@ -2230,29 +2322,45 @@ if build_po:
 
 if 'install' in targets:
     #
-    # install to DESTDIR or prefix
-    dest_dir = env.Dir(env.get('DESTDIR', prefix)).abspath
-    # if dest_dir is different from prefix.
-    if env.has_key('exec_prefix'):
-        bin_dest_dir = Dir(env['exec_prefix']).abspath
-    else:
-        bin_dest_dir = os.path.join(dest_dir, 'bin')
-    if add_suffix:
-        share_dest_dir = os.path.join(dest_dir, share_dir + program_suffix)
-    else:
-        share_dest_dir = os.path.join(dest_dir, share_dir)
-    man_dest_dir = os.path.join(dest_dir, man_dir)
-    locale_dest_dir = os.path.join(dest_dir, locale_dir)
+    # this part is a bit messy right now. Since scons will provide
+    # --DESTDIR option soon, at least the dest_dir handling can be 
+    # removed later.
+    #
+    # how to join dest_dir and prefix
+    def joinPaths(path1, path2):
+        ''' join path1 and path2, do not use os.path.join because
+            under window, c:\destdir\d:\program is invalid '''
+        if path1 is None:
+            return os.path.normpath(path2)
+        # separate drive letter
+        (drive, path) = os.path.splitdrive(os.path.normpath(path2))
+        # ignore drive letter, so c:\destdir + c:\program = c:\destdir\program
+        return os.path.join(os.path.normpath(path1), path[1:])
+    #
+    # install to dest_dir/prefix
+    dest_dir = env.get('DESTDIR', None)
+    dest_prefix_dir = joinPaths(dest_dir, env.Dir(prefix).abspath)
     # create the directory if needed
-    if not os.path.isdir(dest_dir):
+    if not os.path.isdir(dest_prefix_dir):
         try:
-            os.makedirs(dest_dir)
+            os.makedirs(dest_prefix_dir)
         except:
             pass
-        if not os.path.isdir(dest_dir):
-            print 'Can not create directory', dest_dir
+        if not os.path.isdir(dest_prefix_dir):
+            print 'Can not create directory', dest_prefix_dir
             Exit(3)
     #
+    if env.has_key('exec_prefix'):
+        bin_dest_dir = joinPaths(dest_dir, Dir(env['exec_prefix']).abspath)
+    else:
+        bin_dest_dir = os.path.join(dest_prefix_dir, 'bin')
+    if add_suffix:
+        share_dest_dir = os.path.join(dest_prefix_dir, share_dir + program_suffix)
+    else:
+        share_dest_dir = os.path.join(dest_prefix_dir, share_dir)
+    man_dest_dir = os.path.join(dest_prefix_dir, man_dir)
+    locale_dest_dir = os.path.join(dest_prefix_dir, locale_dir)
+    #
     import glob
     #
     # do not install these files
@@ -2273,7 +2381,7 @@ if 'install' in targets:
                 glob.glob(os.path.join(dir, '*'))) )
         return ins_dir
     #
-    # executables (some of them may be none)
+    # install executables (lyxclient may be None)
     #
     if add_suffix:
         version_suffix = program_suffix
@@ -2312,7 +2420,7 @@ if 'install' in targets:
         'templates', 'examples', 'kbd', 'lyx2lyx', 'tex', 'clipart', 'doc',  'ui']]
     )
     # lyx1.4.x does not have lyx2lyx_version.py.in
-    if os.path.isfile('$TOP_SRCDIR/lib/lyx2lyx/lyx2lyx_version.py.in'):
+    if os.path.isfile(env.subst('$TOP_SRCDIR/lib/lyx2lyx/lyx2lyx_version.py.in')):
         # subst and install this file
         env.substFile(share_dest_dir + '/lyx2lyx/lyx2lyx_version.py',
             '$TOP_SRCDIR/lib/lyx2lyx/lyx2lyx_version.py.in')