]> git.lyx.org Git - lyx.git/blob - development/scons/SConstruct
18e8617c4cd6775bb0d60d0eff7ffd17b8f6cd78
[lyx.git] / development / scons / SConstruct
1 # vi:filetype=python:expandtab:tabstop=4:shiftwidth=4
2 #
3 # file SConstruct
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 is a scons based building system for lyx, please refer
12 # to INSTALL.scons for detailed instructions.
13 #
14
15 import os, sys, copy, cPickle, glob, time
16
17 # determine where I am ...
18 #
19 from SCons.Node.FS import default_fs
20 # default_fs.SConstruct_dir is where SConstruct file is located.
21 scons_dir = default_fs.SConstruct_dir.path
22 scons_absdir = default_fs.SConstruct_dir.abspath
23
24 # if SConstruct is copied to the top source directory
25 if os.path.exists(os.path.join(scons_dir, 'development', 'scons', 'scons_manifest.py')):
26     scons_dir = os.path.join(scons_dir, 'development', 'scons')
27     scons_absdir = os.path.join(scons_absdir, 'development', 'scons')
28 # get the ../.. of scons_dir
29 top_src_dir = os.path.split(os.path.split(scons_absdir)[0])[0]
30
31 sys.path.extend([scons_absdir, os.path.join(top_src_dir, 'lib', 'doc')])
32 import depend
33
34 # scons_utils.py defines a few utility function
35 import scons_utils as utils
36 # import all file lists
37 from scons_manifest import *
38
39 #----------------------------------------------------------
40 # Required runtime environment
41 #----------------------------------------------------------
42
43 # scons asks for 1.5.2, lyx requires 2.3
44 EnsurePythonVersion(2, 3)
45 # Please use at least 0.96.92 (not 0.96.1)
46 EnsureSConsVersion(0, 96)
47 # also check for minor version number for scons 0.96
48 from SCons import __version__
49 version = map(int, __version__.split('.'))
50 if version[0] == 0 and version[1] == 96 and version[2] < 92:
51     print "Scons >= 0.96.92 is required."
52     Exit(1)
53
54
55 #----------------------------------------------------------
56 # Global definitions
57 #----------------------------------------------------------
58
59 # some global settings
60 #
61 # get version number from configure.ac so that JMarc does
62 # not have to change SConstruct during lyx release
63 package_version = utils.getVerFromConfigure(top_src_dir)
64 package_cygwin_version = '%s-1' % package_version
65 boost_version = ['1_34']
66
67 if 'svn' in package_version:
68     devel_version = True
69     default_build_mode = 'debug'
70 else:
71     devel_version = False
72     default_build_mode = 'release'
73
74 package = 'lyx'
75 package_bugreport = 'lyx-devel@lists.lyx.org'
76 package_name = 'LyX'
77 package_tarname = 'lyx'
78 package_string = '%s %s' % (package_name, package_version)
79
80 # various cache/log files
81 default_log_file = 'scons_lyx.log'
82 opt_cache_file = 'opt.cache'
83
84
85 #----------------------------------------------------------
86 # platform dependent settings
87 #----------------------------------------------------------
88
89 if os.name == 'nt':
90     platform_name = 'win32'
91     default_prefix = 'c:/program files/lyx'
92     default_with_x = False
93     default_packaging_method = 'windows'
94 elif os.name == 'posix' and sys.platform != 'cygwin':
95     platform_name = sys.platform
96     default_prefix = '/usr/local'
97     default_with_x = True
98     default_packaging_method = 'posix'
99 elif os.name == 'posix' and sys.platform == 'cygwin':
100     platform_name = 'cygwin'
101     default_prefix = '/usr'
102     default_with_x = True
103     default_packaging_method = 'posix'
104 elif os.name == 'darwin':
105     platform_name = 'macosx'
106     # FIXME: macOSX default prefix?
107     default_prefix = '.'
108     default_with_x = False
109     default_packaging_method = 'macosx'
110 else:  # unsupported system, assume posix behavior
111     platform_name = 'others'
112     default_prefix = '.'
113     default_with_x = True
114     default_packaging_method = 'posix'
115
116 #---------------------------------------------------------
117 # Handling options
118 #----------------------------------------------------------
119 #
120 # You can set perminant default values in config.py
121 if os.path.isfile('config.py'):
122     print "Getting options from config.py..."
123     print open('config.py').read()
124
125 opts = Options(['config.py'])
126 opts.AddOptions(
127     # frontend
128     EnumOption('frontend', 'Main GUI', 'qt4',
129         allowed_values = ('qt4',) ),
130     # debug or release build
131     EnumOption('mode', 'Building method', default_build_mode,
132         allowed_values = ('debug', 'release') ),
133     # boost libraries
134     EnumOption('boost',
135         'Use included, system boost library, or try sytem boost first.',
136         'auto', allowed_values = (
137             'auto',       # detect boost, if not found, use included
138             'included',   # always use included boost
139             'system',     # always use system boost, fail if can not find
140             ) ),
141     #
142     EnumOption('gettext',
143         'Use included, system gettext library, or try sytem gettext first',
144         'auto', allowed_values = (
145             'auto',       # detect gettext, if not found, use included
146             'included',   # always use included gettext
147             'system',     # always use system gettext, fail if can not find
148             ) ),
149     #
150     EnumOption('spell', 'Choose spell checker to use.', 'auto',
151         allowed_values = ('aspell', 'pspell', 'ispell', 'auto', 'no') ),
152     # packaging method
153     EnumOption('packaging', 'Packaging method to use.', default_packaging_method,
154         allowed_values = ('windows', 'posix', 'macosx')),
155     #
156     BoolOption('fast_start', 'This option is obsolete.', False),
157     # No precompiled header support (too troublesome to make it work for msvc)
158     # BoolOption('pch', 'Whether or not use pch', False),
159     # enable assertion, (config.h has ENABLE_ASSERTIOS
160     BoolOption('assertions', 'Use assertions', True),
161     # enable warning, (config.h has WITH_WARNINGS)
162     # default to False since MSVC does not have #warning
163     BoolOption('warnings', 'Use warnings', False),
164     # config.h define _GLIBCXX_CONCEPT_CHECKS
165     # Note: for earlier version of gcc (3.3) define _GLIBCPP_CONCEPT_CHECKS
166     BoolOption('concept_checks', 'Enable concept checks', True),
167     #
168     BoolOption('nls', 'Whether or not use native language support', True),
169     #
170     BoolOption('profiling', 'Whether or not enable profiling', False),
171     # config.h define _GLIBCXX_DEBUG and _GLIBCXX_DEBUG_PEDANTIC
172     BoolOption('stdlib_debug', 'Whether or not turn on stdlib debug', False),
173     # using x11?
174     BoolOption('X11', 'Use x11 windows system', default_with_x),
175     # use MS VC++ to build lyx
176     BoolOption('use_vc', 'Use MS VC++ to build lyx (cl.exe will be probed)', None),
177     #
178     PathOption('qt_dir', 'Path to qt directory', None),
179     #
180     PathOption('qt_inc_path', 'Path to qt include directory', None),
181     #
182     PathOption('qt_lib_path', 'Path to qt library directory', None),
183     # extra include and libpath
184     PathOption('extra_inc_path', 'Extra include path', None),
185     #
186     PathOption('extra_lib_path', 'Extra library path', None),
187     #
188     PathOption('extra_bin_path', 'A convenient way to add a path to $PATH', None),
189     #
190     PathOption('extra_inc_path1', 'Extra include path', None),
191     #
192     PathOption('extra_lib_path1', 'Extra library path', None),
193     # rebuild only specifed, comma separated targets
194     ('rebuild', '''rebuild only specifed, comma separated targets.
195         yes or all (default): rebuild everything
196         no or none: rebuild nothing (usually used for installation)
197         comp1,comp2,...: rebuild specified targets''', None),
198     # can be set to a non-existing directory
199     ('prefix', 'install architecture-independent files in PREFIX', default_prefix),
200     # replace the default name and location of the windows installer
201     ('win_installer', 'name or full path to the windows installer', None),
202     # the deps package used to create minimal installer (qt and other libraries)
203     ('deps_dir', 'path to the development depedency packages with zlib, iconv, zlib and qt libraries', None),
204     # whether or not build bundle installer
205     BoolOption('bundle', 'Whether or not build bundle installer', False),
206     # the bundle directory, containing bundled applications
207     PathOption('bundle_dir', 'path to the bundle dependency package with miktex setup.exe etc', None),
208     # build directory, will use $mode if not set
209     ('build_dir', 'Build directory', None),
210     # version suffix
211     ('version_suffix', 'install lyx as lyx-suffix', None),
212     # how to load options
213     ('load_option', '''load option from previous scons run. option can be
214         yes (default): load all options
215         no: do not load any option
216         opt1,opt2: load specified options
217         -opt1,opt2: load all options other than specified ones''', 'yes'),
218     #
219     ('optimization', 'optimization CCFLAGS option.', None),
220     #
221     PathOption('exec_prefix', 'install architecture-independent executable files in PREFIX', None),
222     # log file
223     ('logfile', 'save commands (not outputs) to logfile', default_log_file),
224     # provided for backward compatibility
225     ('dest_dir', 'install to DESTDIR. (Provided for backward compatibility only)', None),
226     # environment variable can be set as options.
227     ('DESTDIR', 'install to DESTDIR', None),
228     ('CC', 'replace default $CC', None),
229     ('LINK', 'replace default $LINK', None),
230     ('CPP', 'replace default $CPP', None),
231     ('CXX', 'replace default $CXX', None),
232     ('CXXCPP', 'replace default $CXXCPP', None),
233     ('CCFLAGS', 'replace default $CCFLAGS', None),
234     ('CPPFLAGS', 'replace default $CPPFLAGS', None),
235     ('LINKFLAGS', 'replace default $LINKFLAGS', None),
236 )
237
238 # allowed options
239 all_options = [x.key for x in opts.options]
240
241 # copied from SCons/Options/BoolOption.py
242 # We need to use them before a boolean ARGUMENTS option is available
243 # in env as bool.
244 true_strings  = ('y', 'yes', 'true', 't', '1', 'on' , 'all' )
245 false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none')
246
247 if ARGUMENTS.has_key('fast_start'):
248     print 'fast_start option is obsolete'
249
250 # if load_option=yes (default), load saved comand line options
251 #
252 # This option can take value yes/no/opt1,opt2/-opt1,opt2
253 # and tries to be clever in choosing options to load
254 if (not ARGUMENTS.has_key('load_option') or \
255     ARGUMENTS['load_option'] not in false_strings) \
256     and os.path.isfile(opt_cache_file):
257     cache_file = open(opt_cache_file)
258     opt_cache = cPickle.load(cache_file)
259     cache_file.close()
260     # import cached options, but we should ignore qt_dir when frontend changes
261     if ARGUMENTS.has_key('frontend') and opt_cache.has_key('frontend') \
262         and ARGUMENTS['frontend'] != opt_cache['frontend'] \
263         and opt_cache.has_key('qt_dir'):
264         opt_cache.pop('qt_dir')
265     # and we do not cache some options (dest_dir is obsolete)
266     for arg in ['load_option', 'dest_dir', 'bundle']:
267         if opt_cache.has_key(arg):
268             opt_cache.pop(arg)
269     # remove obsolete cached keys (well, SConstruct is evolving. :-)
270     for arg in opt_cache.keys():
271         if arg not in all_options:
272             print 'Option %s is obsolete, do not load it' % arg
273             opt_cache.pop(arg)
274     # now, if load_option=opt1,opt2 or -opt1,opt2
275     if ARGUMENTS.has_key('load_option') and \
276         ARGUMENTS['load_option'] not in true_strings + false_strings:
277         # if -opt1,opt2 is specified, do not load these options
278         if ARGUMENTS['load_option'][0] == '-':
279             for arg in ARGUMENTS['load_option'][1:].split(','):
280                 if opt_cache.has_key(arg):
281                     opt_cache.pop(arg)
282         # if opt1,opt2 is specified, only load specified options
283         else:
284             args = ARGUMENTS['load_option'].split(',')
285             for arg in opt_cache.keys():
286                 if arg not in args:
287                     opt_cache.pop(arg)
288     # now restore options as if entered from command line
289     for key in opt_cache.keys():
290         if not ARGUMENTS.has_key(key):
291             ARGUMENTS[key] = opt_cache[key]
292             print "Restoring cached option  %s=%s" % (key, ARGUMENTS[key])
293     print
294
295 # check if there is unused (or misspelled) argument
296 for arg in ARGUMENTS.keys():
297     if arg not in all_options:
298         import textwrap
299         print "Unknown option '%s'... exiting." % arg
300         print
301         print "Available options are (check 'scons -help' for details):"
302         print '    ' + '\n    '.join(textwrap.wrap(',  '.join(all_options)))
303         Exit(1)
304
305 # save options used
306 cache_file = open(opt_cache_file, 'w')
307 cPickle.dump(ARGUMENTS, cache_file)
308 cache_file.close()
309
310 #---------------------------------------------------------
311 # Setting up environment
312 #---------------------------------------------------------
313
314 # I do not really like ENV=os.environ, but you may add it
315 # here if you experience some environment related problem
316 env = Environment(options = opts)
317
318 # set individual variables since I do not really like ENV = os.environ
319 env['ENV']['PATH'] = os.environ.get('PATH')
320 env['ENV']['HOME'] = os.environ.get('HOME')
321 # these are defined for MSVC
322 env['ENV']['LIB'] = os.environ.get('LIB')
323 env['ENV']['INCLUDE'] = os.environ.get('INCLUDE')
324
325 # for simplicity, use var instead of env[var]
326 frontend = env['frontend']
327 prefix = env['prefix']
328 mode = env['mode']
329
330 if platform_name == 'win32':
331     if env.has_key('use_vc'):
332         use_vc = env['use_vc']
333         if WhereIs('cl.exe') is None:
334             print "cl.exe is not found. Are you using the MSVC environment?"
335             Exit(2)
336     elif WhereIs('cl.exe') is not None:
337         use_vc = True
338     else:
339         use_vc = False
340 else:
341     use_vc = False
342
343 # lyx will be built to $build/build_dir so it is possible
344 # to build multiple build_dirs using the same source
345 # $mode can be debug or release
346 if env.has_key('build_dir') and env['build_dir'] is not None:
347     # create the directory if needed
348     if not os.path.isdir(env['build_dir']):
349         try:
350             os.makedirs(env['build_dir'])
351         except:
352             pass
353         if not os.path.isdir(env['build_dir']):
354             print 'Can not create directory', env['build_dir']
355             Exit(3)
356     env['BUILDDIR'] = env['build_dir']
357 else:
358     # Determine the name of the build $mode
359     env['BUILDDIR'] = '#' + mode
360
361 # all built libraries will go to build_dir/libs
362 # (This is different from the make file approach)
363 env['LOCALLIBPATH'] = '$BUILDDIR/libs'
364 env.AppendUnique(LIBPATH = ['$LOCALLIBPATH'])
365
366
367 # Here is a summary of variables defined in env
368 # 1. defined options
369 # 2. undefined options with a non-None default value
370 # 3. compiler commands and flags like CCFLAGS.
371 #     MSGFMT used to process po files
372 # 4. Variables that will be used to replace variables in some_file.in
373 #     src/support/Package.cpp.in:
374 #       TOP_SRCDIR, LOCALEDIR, LYX_DIR, PROGRAM_SUFFIX
375 #     lib/lyx2lyx/lyx2lyx_version.py.in
376 #       PACKAGE_VERSION
377 #     src/version.cpp.in
378 #       PACKAGE_VERSION, LYX_DATE, VERSION_INFO
379
380 # full path name is used to build msvs project files
381 # and to replace TOP_SRCDIR in package.C
382 env['TOP_SRCDIR'] = Dir(top_src_dir).abspath
383 # needed by src/version.cpp.in => src/version.cpp
384 env['PACKAGE_VERSION'] = package_version
385 env['LYX_DATE'] = time.asctime()
386
387 # determine share_dir etc
388 packaging_method = env.get('packaging')
389 if packaging_method == 'windows':
390     share_dir = 'Resources'
391     man_dir = 'Resources/man/man1'
392     locale_dir = 'Resources/locale'
393 else:
394     share_dir = 'share/lyx'
395     locale_dir = 'share/locale'
396     if platform_name == 'cygwin':
397         man_dir = 'share/man/man1'
398     else:
399         man_dir = 'man/man1'
400
401 # program suffix: can be yes, or a string
402 if env.has_key('version_suffix'):
403     if env['version_suffix'] in true_strings:
404         program_suffix = package_version
405     elif env['version_suffix'] in false_strings:
406         program_suffix = ''
407     else:
408         program_suffix = env['version_suffix']
409 else:
410     program_suffix = ''
411 # used by Package.cpp.in
412 env['PROGRAM_SUFFIX'] = program_suffix
413
414 # whether or not add suffix to file and directory names
415 add_suffix = packaging_method != 'windows'
416 # LYX_DIR are different (used in Package.cpp.in)
417 if add_suffix:
418     env['LYX_DIR'] = Dir(os.path.join(prefix, share_dir + program_suffix)).abspath
419 else:
420     env['LYX_DIR'] = Dir(os.path.join(prefix, share_dir)).abspath
421 # we need absolute path for package.C
422 env['LOCALEDIR'] = Dir(os.path.join(prefix, locale_dir)).abspath
423
424
425 #---------------------------------------------------------
426 # Setting building environment (Tools, compiler flags etc)
427 #---------------------------------------------------------
428
429 # Since Tool('mingw') will reset CCFLAGS etc, this should be
430 # done before getEnvVariable
431 if platform_name == 'win32':
432     if use_vc:
433         env.Tool('msvc')
434         env.Tool('mslink')
435     else:
436         env.Tool('mingw')
437         env.AppendUnique(CPPPATH = ['#c:/MinGW/include'])
438         # fix a scons winres bug (there is a missing space between ${RCINCPREFIX} and ${SOURCE.dir}
439         # in version 0.96.93
440         env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET'
441     
442
443 # we differentiate between hard-coded options and default options
444 # hard-coded options are required and will always be there
445 # default options can be replaced by enviromental variables or command line options
446 CCFLAGS_required = []
447 LINKFLAGS_required = []
448 CCFLAGS_default = []
449
450 # under windows, scons is confused by .C/.c and uses gcc instead of
451 # g++. I am forcing the use of g++ here. This is expected to change
452 # after lyx renames all .C files to .cpp
453 #
454 # save the old c compiler and CCFLAGS (used by libintl)
455 C_COMPILER = env.subst('$CC')
456 C_CCFLAGS = env.subst('$CCFLAGS').split()
457 # if we use ms vc, the commands are fine (cl.exe and link.exe)
458 if use_vc:
459     # /TP treat all source code as C++
460     # C4819: The file contains a character that cannot be represented
461     #   in the current code page (number)
462     # C4996: foo was decleared deprecated
463     CCFLAGS_required.extend(['/TP', '/EHsc'])
464     if mode == 'debug':
465         CCFLAGS_default.extend(['/wd4819', '/wd4996', '/nologo', '/MDd'])
466         # the flags are also needed in C mode (for intl lib)
467         C_CCFLAGS.extend(['/wd4819', '/wd4996', '/nologo', '/MDd'])
468     else:
469         CCFLAGS_default.extend(['/wd4819', '/wd4996', '/nologo', '/MD'])
470         C_CCFLAGS.extend(['/wd4819', '/wd4996', '/nologo', '/MD'])
471 else:
472     if env.has_key('CXX') and env['CXX']:
473         env['CC'] = env.subst('$CXX')
474         env['LINK'] = env.subst('$CXX')
475     else:
476         env['CC'] = 'g++'
477         env['LINK'] = 'g++'
478
479 # for debug/release mode
480 if env.has_key('optimization') and env['optimization'] is not None:
481     # if user supplies optimization flags, use it anyway
482     CCFLAGS_required.extend(env['optimization'].split())
483     # and do not use default
484     set_default_optimization_flags = False
485 else:
486     set_default_optimization_flags = True
487
488 if mode == 'debug':
489     if use_vc:
490         CCFLAGS_required.append('/Zi')
491         LINKFLAGS_required.extend(['/debug', '/map'])
492     else:
493         CCFLAGS_required.append('-g')
494         CCFLAGS_default.append('-O')
495 elif mode == 'release' and set_default_optimization_flags:
496     if use_vc:
497         CCFLAGS_default.append('/O2')
498     else:
499         CCFLAGS_default.append('-O2')
500
501 # msvc uses separate tools for profiling
502 if env.has_key('profiling') and env['profiling']:
503     if use_vc:
504         print 'Visual C++ does not use profiling options'
505     else:
506         CCFLAGS_required.append('-pg')
507         LINKFLAGS_required.append('-pg')
508
509 if env.has_key('warnings') and env['warnings']:
510     if use_vc:
511         CCFLAGS_default.append('/W2')
512     else:
513         # Note: autotools detect gxx version and pass -W for 3.x
514         # and -Wextra for other versions of gcc
515         CCFLAGS_default.append('-Wall')
516
517 # Now, set the variables as follows:
518 # 1. if command line option exists: replace default
519 # 2. then if s envronment variable exists: replace default
520 # 3. set variable to required + default
521 def setEnvVariable(env, name, required = None, default = None, split = True):
522     ''' env: environment to set variable
523             name: variable
524             required: hardcoded options
525             default: default options that can be replaced by command line or
526                 environment variables
527             split: whether or not split obtained variable like '-02 -g'
528     '''
529     # 1. ARGUMENTS is already set to env[name], override default.
530     if ARGUMENTS.has_key(name):
531         # env[name] may be rewritten when building tools are reloaded
532         # if that is the case, commandline option will override it.
533         env[name] = ARGUMENTS[name]
534         default = None
535     # then use environment default
536     elif os.environ.has_key(name):
537         print "Acquiring variable %s from system environment: %s" % (name, os.environ[name])
538         default = os.environ[name]
539         if split:
540             default = default.split()
541     # the real value should be env[name] + default + required
542     if split:
543         value = []
544         if env.has_key(name):
545             value = str(env[name]).split()
546         if required is not None:
547             value += required
548         if default is not None:
549             value += default
550     else:
551         value = ""
552         if env.has_key(name):
553             value = str(env[name])
554         if required is not None:
555             value += " " + required
556         if default is not None:
557             value += " " + default
558     env[name] = value
559     # print name, env[name]
560
561 setEnvVariable(env, 'DESTDIR', split=False)
562 setEnvVariable(env, 'CC')
563 setEnvVariable(env, 'LINK')
564 setEnvVariable(env, 'CPP')
565 setEnvVariable(env, 'CXX')
566 setEnvVariable(env, 'CXXCPP')
567 setEnvVariable(env, 'CCFLAGS', CCFLAGS_required, CCFLAGS_default)
568 setEnvVariable(env, 'CXXFLAGS')
569 setEnvVariable(env, 'CPPFLAGS')
570 setEnvVariable(env, 'LINKFLAGS', LINKFLAGS_required)
571
572 # if DESTDIR is not set...
573 if env.has_key('dest_dir'):
574     print "This option is obsolete. Please use DESTDIR instead."
575     env['DESTDIR'] = env['dest_dir']
576
577 #
578 # extra_inc_path and extra_lib_path
579 #
580 extra_inc_paths = []
581 if env.has_key('extra_inc_path') and env['extra_inc_path']:
582     extra_inc_paths.append(env['extra_inc_path'])
583 if env.has_key('extra_lib_path') and env['extra_lib_path']:
584     env.AppendUnique(LIBPATH = [env['extra_lib_path']])
585 if env.has_key('extra_inc_path1') and env['extra_inc_path1']:
586     extra_inc_paths.append(env['extra_inc_path1'])
587 if env.has_key('extra_lib_path1') and env['extra_lib_path1']:
588     env.AppendUnique(LIBPATH = [env['extra_lib_path1']])
589 if env.has_key('extra_bin_path') and env['extra_bin_path']:
590     # only the first one is needed (a scons bug?)
591     os.environ['PATH'] += os.pathsep + env['extra_bin_path']
592     env.PrependENVPath('PATH', env['extra_bin_path'])
593 # extra_inc_paths will be used later by intlenv etc
594 env.AppendUnique(CPPPATH = extra_inc_paths)
595
596
597 #----------------------------------------------------------
598 # Autoconf business
599 #----------------------------------------------------------
600
601 conf = Configure(env,
602     custom_tests = {
603         'CheckPkgConfig' : utils.checkPkgConfig,
604         'CheckPackage' : utils.checkPackage,
605         'CheckMkdirOneArg' : utils.checkMkdirOneArg,
606         'CheckSelectArgType' : utils.checkSelectArgType,
607         'CheckBoostLibraries' : utils.checkBoostLibraries,
608         'CheckCommand' : utils.checkCommand,
609         'CheckNSIS' : utils.checkNSIS,
610         'CheckCXXGlobalCstd' : utils.checkCXXGlobalCstd,
611         'CheckLC_MESSAGES' : utils.checkLC_MESSAGES,
612         'CheckIconvConst' : utils.checkIconvConst,
613         'CheckSizeOfWChar' : utils.checkSizeOfWChar,
614         'CheckDeclaration' : utils.checkDeclaration,
615     }
616 )
617
618 # When using msvc, windows.h is required
619 if use_vc and not conf.CheckCHeader('windows.h'):
620     print 'Windows.h is not found. Please install Windows Platform SDK.'
621     Exit(1)
622
623 # pkg-config? (if not, we use hard-coded options)
624 if conf.CheckPkgConfig('0.15.0'):
625     env['HAS_PKG_CONFIG'] = True
626 else:
627     print 'pkg-config >= 0.1.50 is not found'
628     env['HAS_PKG_CONFIG'] = False
629
630 # zlib? This is required.
631 if (not use_vc and not conf.CheckLibWithHeader('z', 'zlib.h', 'C')) \
632     or (use_vc and not conf.CheckLibWithHeader('zdll', 'zlib.h', 'C')):
633     print 'Did not find zdll.lib or zlib.h, exiting!'
634     Exit(1)
635 if conf.CheckLib('iconv'):
636     env['ICONV_LIB'] = 'iconv'
637 elif conf.CheckLib('libiconv'):
638     env['ICONV_LIB'] = 'libiconv'
639 elif conf.CheckFunc('iconv_open'):
640     env['ICONV_LIB'] = None
641 else:
642     print 'Did not find iconv or libiconv, exiting!'
643     Exit(1)
644
645 # check socket libs
646 socket_libs = []
647 if conf.CheckLib('socket'):
648     socket_libs.append('socket')
649 # nsl is the network services library and provides a
650 # transport-level interface to networking services.
651 if conf.CheckLib('nsl'):
652     socket_libs.append('nsl')
653
654 # check available boost libs (since lyx1.4 does not use iostream)
655 boost_libs = []
656 for lib in ['signals', 'regex', 'filesystem', 'iostreams']:
657     if os.path.isdir(os.path.join(top_src_dir, 'boost', 'libs', lib)):
658         boost_libs.append(lib)
659
660 # check boost libraries
661 boost_opt = ARGUMENTS.get('boost', 'auto')
662 # check for system boost
663 lib_paths = env['LIBPATH'] + ['/usr/lib', '/usr/local/lib']
664 inc_paths = env['CPPPATH'] + ['/usr/include', '/usr/local/include']
665 # default to $BUILDDIR/libs (use None since this path will be added anyway)
666 boost_libpath = None
667 # here I assume that all libraries are in the same directory
668 if boost_opt == 'included':
669     boost_libraries = ['included_boost_%s' % x for x in boost_libs]
670     included_boost = True
671     env['BOOST_INC_PATH'] = '$TOP_SRCDIR/boost'
672 elif boost_opt == 'auto':
673     res = conf.CheckBoostLibraries(boost_libs, lib_paths, inc_paths, boost_version, mode == 'debug')
674     # if not found, use local boost
675     if res[0] is None:
676         boost_libraries = ['included_boost_%s' % x for x in boost_libs]
677         included_boost = True
678         env['BOOST_INC_PATH'] = '$TOP_SRCDIR/boost'
679     else:
680         included_boost = False
681         (boost_libraries, boost_libpath, env['BOOST_INC_PATH']) = res
682 elif boost_opt == 'system':
683     res = conf.CheckBoostLibraries(boost_libs, lib_paths, inc_paths, boost_version, mode == 'debug')
684     if res[0] is None:
685         print "Can not find system boost libraries with version %s " % boost_version
686         print "Please supply a path through extra_lib_path and try again."
687         print "Or use boost=included to use included boost libraries."
688         Exit(2)
689     else:
690         included_boost = False
691         (boost_libraries, boost_libpath, env['BOOST_INC_PATH']) = res
692
693
694 if boost_libpath is not None:
695     env.AppendUnique(LIBPATH = [boost_libpath])
696
697
698 env['ENABLE_NLS'] = env['nls']
699
700 if not env['ENABLE_NLS']:
701     intl_libs = []
702     included_gettext = False
703 else:
704     # check gettext libraries
705     gettext_opt = ARGUMENTS.get('gettext', 'auto')
706     # check for system gettext
707     succ = False
708     if gettext_opt in ['auto', 'system']:
709         if conf.CheckFunc('gettext'):
710             included_gettext = False
711             intl_libs = []
712             succ = True
713         elif conf.CheckLib('intl'):
714             included_gettext = False
715             intl_libs = ['intl']
716             succ = True
717         else: # no found
718             if gettext_opt == 'system':
719                 print "Can not find system gettext library"
720                 print "Please supply a path through extra_lib_path and try again."
721                 print "Or use gettext=included to use included gettext libraries."
722                 Exit(2)
723     # now, auto and succ = false, or gettext=included
724     if not succ:
725         # we do not need to set LIBPATH now.
726         included_gettext = True
727         intl_libs = ['included_intl']
728
729
730 #
731 # check for msgfmt command
732 env['MSGFMT'] = conf.CheckCommand('msgfmt')
733 env['MSGMERGE'] = conf.CheckCommand('msgmerge')
734 env['XGETTEXT'] = conf.CheckCommand('xgettext')
735 env['MSGUNIQ'] = conf.CheckCommand('msguniq')
736
737 # if under windows, check the nsis compiler
738 if platform_name == 'win32':
739     env['NSIS'] = conf.CheckNSIS()
740
741 # cygwin packaging requires the binaries to be stripped
742 if platform_name == 'cygwin':
743     env['STRIP'] = conf.CheckCommand('strip')
744
745 #
746 # Customized builders
747 #
748 # install customized builders
749 env['BUILDERS']['substFile'] = Builder(action = utils.env_subst)
750 env['BUILDERS']['installTOC'] = Builder(action = utils.env_toc)
751 env['BUILDERS']['potfiles'] = Builder(action = utils.env_potfiles)
752
753
754 #----------------------------------------------------------
755 # Generating config.h
756 #----------------------------------------------------------
757 aspell_lib = 'aspell'
758 # assume that we use aspell, aspelld compiled for msvc
759 if platform_name == 'win32' and mode == 'debug' and use_vc:
760     aspell_lib = 'aspelld'
761
762 # check the existence of config.h
763 config_h = os.path.join(env.Dir('$BUILDDIR/common').path, 'config.h')
764 boost_config_h = os.path.join(env.Dir('$BUILDDIR/boost').path, 'config.h')
765 #
766 print "Creating %s..." % boost_config_h
767 #
768 utils.createConfigFile(conf,
769     config_file = boost_config_h,
770     config_pre = '''/* boost/config.h.  Generated by SCons.  */
771
772 /* -*- C++ -*- */
773 /*
774 * \file config.h
775 * This file is part of LyX, the document processor.
776 * Licence details can be found in the file COPYING.
777 *
778 * This is the compilation configuration file for LyX.
779 * It was generated by scon.
780 * You might want to change some of the defaults if something goes wrong
781 * during the compilation.
782 */
783
784 #ifndef _BOOST_CONFIG_H
785 #define _BOOST_CONFIG_H
786 ''',
787     headers = [
788         ('ostream', 'HAVE_OSTREAM', 'cxx'),
789         ('locale', 'HAVE_LOCALE', 'cxx'),
790         ('sstream', 'HAVE_SSTREAM', 'cxx'),
791         #('newapis.h', 'HAVE_NEWAPIS_H', 'c'),
792     ],
793     custom_tests = [
794         (env.has_key('assertions') and env['assertions'],
795             'ENABLE_ASSERTIONS',
796             'Define if you want assertions to be enabled in the code'
797         ),
798     ],
799     types = [
800         ('wchar_t', 'HAVE_WCHAR_T', None),
801     ],
802     config_post = '''
803
804 #if defined(HAVE_OSTREAM) && defined(HAVE_LOCALE) && defined(HAVE_SSTREAM)
805 #  define USE_BOOST_FORMAT 1
806 #else
807 #  define USE_BOOST_FORMAT 0
808 #endif
809
810 #if !defined(ENABLE_ASSERTIONS)
811 #  define BOOST_DISABLE_ASSERTS 1
812 #endif
813 #define BOOST_ENABLE_ASSERT_HANDLER 1
814
815 #define BOOST_DISABLE_THREADS 1
816 #define BOOST_NO_WSTRING 1
817
818 #ifdef __CYGWIN__
819 #  define BOOST_POSIX 1
820 #  define BOOST_POSIX_API 1
821 #  define BOOST_POSIX_PATH 1
822 #endif
823
824 #define BOOST_ALL_NO_LIB 1
825
826 #if defined(HAVE_NEWAPIS_H)
827 #  define WANT_GETFILEATTRIBUTESEX_WRAPPER 1
828 #endif
829
830 /*
831  * the FreeBSD libc uses UCS4, but libstdc++ has no proper wchar_t
832  * support compiled in:
833  * http://gcc.gnu.org/onlinedocs/libstdc++/faq/index.html#3_9
834  * And we are not interested at all what libc
835  * does: What we need is a 32bit wide wchar_t, and a libstdc++ that
836  * has the needed wchar_t support and uses UCS4. Whether it
837  * implements this with the help of libc, or whether it has own code
838  * does not matter for us, because we don't use libc directly (Georg)
839 */
840 #if defined(HAVE_WCHAR_T) && SIZEOF_WCHAR_T == 4 && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
841 #  define USE_WCHAR_T
842 #endif
843
844 #endif
845 '''
846 )
847 #
848 print "\nGenerating %s..." % config_h
849
850 # AIKSAURUS_H_LOCATION
851 if (conf.CheckCXXHeader("Aiksaurus.h")):
852     aik_location = '<Aiksaurus.h>'
853 elif (conf.CheckCXXHeader("Aiksaurus/Aiksaurus.h")):
854     aik_location = '<Aiksaurus/Aiksaurus.h>'
855 else:
856     aik_location = ''
857
858 # determine headers to use
859 spell_opt = ARGUMENTS.get('spell', 'auto')
860 env['USE_ASPELL'] = False
861 env['USE_PSPELL'] = False
862 env['USE_ISPELL'] = False
863 if spell_opt in ['auto', 'aspell'] and conf.CheckLib(aspell_lib):
864     spell_engine = 'USE_ASPELL'
865 elif spell_opt in ['auto', 'pspell'] and conf.CheckLib('pspell'):
866     spell_engine = 'USE_PSPELL'
867 elif spell_opt in ['auto', 'ispell'] and conf.CheckLib('ispell'):
868     spell_engine = 'USE_ISPELL'
869 else:
870     spell_engine = None
871
872 if spell_engine is not None:
873     env[spell_engine] = True
874 else:
875     if spell_opt == 'auto':
876         print "Warning: Can not locate any spell checker"
877     elif spell_opt != 'no':
878         print "Warning: Can not locate specified spell checker:", spell_opt
879         Exit(1)
880
881 # check arg types of select function
882 (select_arg1, select_arg234, select_arg5) = conf.CheckSelectArgType()
883
884 # check the size of wchar_t
885 sizeof_wchar_t = conf.CheckSizeOfWChar()
886 # something wrong
887 if sizeof_wchar_t == 0:
888     print 'Error: Can not determine the size of wchar_t.'
889     Exit(1)
890
891 #
892 # create config.h
893 result = utils.createConfigFile(conf,
894     config_file = config_h,
895     config_pre = '''/* config.h.  Generated by SCons.  */
896
897 /* -*- C++ -*- */
898 /*
899 * \file config.h
900 * This file is part of LyX, the document processor.
901 * Licence details can be found in the file COPYING.
902 *
903 * This is the compilation configuration file for LyX.
904 * It was generated by scon.
905 * You might want to change some of the defaults if something goes wrong
906 * during the compilation.
907 */
908
909 #ifndef _CONFIG_H
910 #define _CONFIG_H
911 ''',
912     headers = [
913         ('io.h', 'HAVE_IO_H', 'c'),
914         ('limits.h', 'HAVE_LIMITS_H', 'c'),
915         ('locale.h', 'HAVE_LOCALE_H', 'c'),
916         ('process.h', 'HAVE_PROCESS_H', 'c'),
917         ('stdlib.h', 'HAVE_STDLIB_H', 'c'),
918         ('sys/stat.h', 'HAVE_SYS_STAT_H', 'c'),
919         ('sys/time.h', 'HAVE_SYS_TIME_H', 'c'),
920         ('sys/types.h', 'HAVE_SYS_TYPES_H', 'c'),
921         ('sys/utime.h', 'HAVE_SYS_UTIME_H', 'c'),
922         ('sys/socket.h', 'HAVE_SYS_SOCKET_H', 'c'),
923         ('unistd.h', 'HAVE_UNISTD_H', 'c'),
924         ('utime.h', 'HAVE_UTIME_H', 'c'),
925         ('direct.h', 'HAVE_DIRECT_H', 'c'),
926         ('istream', 'HAVE_ISTREAM', 'cxx'),
927         ('ios', 'HAVE_IOS', 'cxx'),
928     ],
929     functions = [
930         ('open', 'HAVE_OPEN', None),
931         ('chmod', 'HAVE_CHMOD', None),
932         ('close', 'HAVE_CLOSE', None),
933         ('popen', 'HAVE_POPEN', None),
934         ('pclose', 'HAVE_PCLOSE', None),
935         ('_open', 'HAVE__OPEN', None),
936         ('_close', 'HAVE__CLOSE', None),
937         ('_popen', 'HAVE__POPEN', None),
938         ('_pclose', 'HAVE__PCLOSE', None),
939         ('getpid', 'HAVE_GETPID', None),
940         ('_getpid', 'HAVE__GETPID', None),
941         ('mkdir', 'HAVE_MKDIR', None),
942         ('_mkdir', 'HAVE__MKDIR', None),
943         ('mktemp', 'HAVE_MKTEMP', None),
944         ('mkstemp', 'HAVE_MKSTEMP', None),
945         ('strerror', 'HAVE_STRERROR', None),
946         ('count', 'HAVE_STD_COUNT', '''
947 #include <algorithm>
948 int count()
949 {
950 char a[] = "hello";
951 return std::count(a, a+5, 'l');
952 }
953 '''),
954         ('getcwd', 'HAVE_GETCWD', None),
955         ('setenv', 'HAVE_SETENV', None),
956         ('putenv', 'HAVE_PUTENV', None),
957         ('fcntl', 'HAVE_FCNTL', None),
958         ('mkfifo', 'HAVE_MKFIFO', None),
959     ],
960     declarations = [
961         ('mkstemp', 'HAVE_DECL_MKSTEMP', ['unistd.h', 'stdlib.h']),
962     ],
963     types = [
964         ('std::istreambuf_iterator<std::istream>', 'HAVE_DECL_ISTREAMBUF_ITERATOR',
965             '#include <streambuf>\n#include <istream>'),
966         ('wchar_t', 'HAVE_WCHAR_T', None),
967         ('mode_t', 'HAVE_MODE_T', "#include <sys/types.h>"),
968     ],
969     libs = [
970         ('gdi32', 'HAVE_LIBGDI32'),
971         (('Aiksaurus', 'libAiksaurus'), 'HAVE_LIBAIKSAURUS', 'AIKSAURUS_LIB'),
972     ],
973     custom_tests = [
974         (conf.CheckType('pid_t', includes='#include <sys/types.h>'),
975             'HAVE_PID_T',
976             'Define is sys/types.h does not have pid_t',
977             '',
978             '#define pid_t int',
979         ),
980         (conf.CheckCXXGlobalCstd(),
981             'CXX_GLOBAL_CSTD',
982             'Define if your C++ compiler puts C library functions in the global namespace'
983         ),
984         (conf.CheckMkdirOneArg(),
985             'MKDIR_TAKES_ONE_ARG',
986             'Define if mkdir takes only one argument.'
987         ),
988         (conf.CheckIconvConst(),
989             'ICONV_CONST',
990             'Define as const if the declaration of iconv() needs const.',
991             '#define ICONV_CONST const',
992             '#define ICONV_CONST',
993         ),
994         (conf.CheckLC_MESSAGES(),
995             'HAVE_LC_MESSAGES',
996             'Define if your <locale.h> file defines LC_MESSAGES.'
997         ),
998         (devel_version, 'DEVEL_VERSION', 'Whether or not a development version'),
999         (env['nls'],
1000             'ENABLE_NLS',
1001             "Define to 1 if translation of program messages to the user's native anguage is requested.",
1002         ),
1003         (env['nls'] and not included_gettext,
1004             'HAVE_GETTEXT',
1005             'Define to 1 if using system gettext library'
1006         ),
1007         (env.has_key('warnings') and env['warnings'],
1008             'WITH_WARNINGS',
1009             'Define this if you want to see the warning directives put here and there by the developpers to get attention'
1010         ),
1011         (env.has_key('concept_checks') and env['concept_checks'],
1012             '_GLIBCXX_CONCEPT_CHECKS',
1013             'libstdc++ concept checking'
1014         ),
1015         (env.has_key('stdlib_debug') and env['stdlib_debug'],
1016             '_GLIBCXX_DEBUG',
1017             'libstdc++ debug mode'
1018         ),
1019         (env.has_key('stdlib_debug') and env['stdlib_debug'],
1020             '_GLIBCXX_DEBUG_PEDANTIC',
1021             'libstdc++ pedantic debug mode'
1022         ),
1023         (os.name != 'nt', 'BOOST_POSIX',
1024             'Indicates to boost < 1.34 which API to use (posix or windows).'
1025         ),
1026         (os.name != 'nt', 'BOOST_POSIX_API',
1027             'Indicates to boost 1.34 which API to use (posix or windows).'
1028         ),
1029         (os.name != 'nt', 'BOOST_POSIX_PATH',
1030             'Indicates to boost 1.34 which path style to use (posix or windows).'
1031         ),
1032         (spell_engine is not None, spell_engine,
1033             'Spell engine to use'
1034         ),
1035         # we need to know the byte order for unicode conversions
1036         (sys.byteorder == 'big', 'WORDS_BIGENDIAN',
1037             'Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX).'
1038         ),
1039     ],
1040     extra_items = [
1041         ('#define PACKAGE "%s%s"' % (package, program_suffix),
1042             'Name of package'),
1043         ('#define PACKAGE_BUGREPORT "%s"' % package_bugreport,
1044             'Define to the address where bug reports for this package should be sent.'),
1045         ('#define PACKAGE_NAME "%s"' % package_name,
1046             'Define to the full name of this package.'),
1047         ('#define PACKAGE_STRING "%s"' % package_string,
1048             'Define to the full name and version of this package.'),
1049         ('#define PACKAGE_TARNAME "%s"' % package_tarname,
1050             'Define to the one symbol short name of this package.'),
1051         ('#define PACKAGE_VERSION "%s"' % package_version,
1052             'Define to the version of this package.'),
1053         ('#define BOOST_ALL_NO_LIB 1',
1054             'disable automatic linking of boost libraries.'),
1055         ('#define USE_%s_PACKAGING 1' % packaging_method.upper(),
1056             'Packaging method'),
1057         ('#define AIKSAURUS_H_LOCATION ' + aik_location,
1058             'Aiksaurus include file'),
1059         ('#define SELECT_TYPE_ARG1 %s' % select_arg1,
1060             "Define to the type of arg 1 for `select'."),
1061         ('#define SELECT_TYPE_ARG234 %s' % select_arg234,
1062             "Define to the type of arg 2, 3, 4 for `select'."),
1063         ('#define SELECT_TYPE_ARG5 %s' % select_arg5,
1064             "Define to the type of arg 5 for `select'."),
1065         ('#define SIZEOF_WCHAR_T %d' % sizeof_wchar_t,
1066             'Define to be the size of type wchar_t'),
1067     ],
1068     config_post = '''/************************************************************
1069 ** You should not need to change anything beyond this point */
1070
1071 #ifndef HAVE_STRERROR
1072 #if defined(__cplusplus)
1073 extern "C"
1074 #endif
1075 char * strerror(int n);
1076 #endif
1077
1078 #ifdef HAVE_MKSTEMP
1079 #ifndef HAVE_DECL_MKSTEMP
1080 #if defined(__cplusplus)
1081 extern "C"
1082 #endif
1083 int mkstemp(char*);
1084 #endif
1085 #endif
1086
1087 #include <../boost/config.h>
1088
1089 #endif
1090 '''
1091 )
1092
1093 # these keys are needed in env
1094 for key in ['USE_ASPELL', 'USE_PSPELL', 'USE_ISPELL', 'HAVE_FCNTL',\
1095     'HAVE_LIBGDI32', 'HAVE_LIBAIKSAURUS', 'AIKSAURUS_LIB']:
1096     # USE_ASPELL etc does not go through result
1097     if result.has_key(key):
1098         env[key] = result[key]
1099
1100 #
1101 # if nls=yes and gettext=included, create intl/config.h
1102 # intl/libintl.h etc
1103 #
1104 intl_config_h = os.path.join(env.Dir('$BUILDDIR/intl').path, 'config.h')
1105 if env['nls'] and included_gettext:
1106     #
1107     print "Creating %s..." % intl_config_h
1108     #
1109     # create intl/config.h
1110     result = utils.createConfigFile(conf,
1111         config_file = intl_config_h,
1112         config_pre = '''/* intl/config.h.  Generated by SCons.  */
1113
1114 /* -*- C++ -*- */
1115 /*
1116 * \file config.h
1117 * This file is part of LyX, the document processor.
1118 * Licence details can be found in the file COPYING.
1119 *
1120 * This is the compilation configuration file for LyX.
1121 * It was generated by scon.
1122 * You might want to change some of the defaults if something goes wrong
1123 * during the compilation.
1124 */
1125
1126 #ifndef _CONFIG_H
1127 #define _CONFIG_H
1128 ''',
1129         headers = [
1130             ('unistd.h', 'HAVE_UNISTD_H', 'c'),
1131             ('inttypes.h', 'HAVE_INTTYPES_H', 'c'),
1132             ('string.h', 'HAVE_STRING_H', 'c'),
1133             ('strings.h', 'HAVE_STRINGS_H', 'c'),
1134             ('argz.h', 'HAVE_ARGZ_H', 'c'),
1135             ('limits.h', 'HAVE_LIMITS_H', 'c'),
1136             ('alloca.h', 'HAVE_ALLOCA_H', 'c'),
1137             ('stddef.h', 'HAVE_STDDEF_H', 'c'),
1138             ('stdint.h', 'HAVE_STDINT_H', 'c'),
1139             ('sys/param.h', 'HAVE_SYS_PARAM_H', 'c'),
1140         ],
1141         functions = [
1142             ('getcwd', 'HAVE_GETCWD', None),
1143             ('stpcpy', 'HAVE_STPCPY', None),
1144             ('strcasecmp', 'HAVE_STRCASECMP', None),
1145             ('strdup', 'HAVE_STRDUP', None),
1146             ('strtoul', 'HAVE_STRTOUL', None),
1147             ('alloca', 'HAVE_ALLOCA', None),
1148             ('__fsetlocking', 'HAVE___FSETLOCKING', None),
1149             ('mempcpy', 'HAVE_MEMPCPY', None),
1150             ('__argz_count', 'HAVE___ARGZ_COUNT', None),
1151             ('__argz_next', 'HAVE___ARGZ_NEXT', None),
1152             ('__argz_stringify', 'HAVE___ARGZ_STRINGIFY', None),
1153             ('setlocale', 'HAVE_SETLOCALE', None),
1154             ('tsearch', 'HAVE_TSEARCH', None),
1155             ('getegid', 'HAVE_GETEGID', None),
1156             ('getgid', 'HAVE_GETGID', None),
1157             ('getuid', 'HAVE_GETUID', None),
1158             ('wcslen', 'HAVE_WCSLEN', None),
1159             ('asprintf', 'HAVE_ASPRINTF', None),
1160             ('wprintf', 'HAVE_WPRINTF', None),
1161             ('snprintf', 'HAVE_SNPRINTF', None),
1162             ('printf', 'HAVE_POSIX_PRINTF', None),
1163             ('fcntl', 'HAVE_FCNTL', None),
1164         ],
1165         types = [
1166             ('intmax_t', 'HAVE_INTMAX_T', None),
1167             ('long double', 'HAVE_LONG_DOUBLE', None),
1168             ('long long', 'HAVE_LONG_LONG', None),
1169             ('wchar_t', 'HAVE_WCHAR_T', None),
1170             ('wint_t', 'HAVE_WINT_T', None),
1171             ('uintmax_t', 'HAVE_INTTYPES_H_WITH_UINTMAX', '#include <inttypes.h>'),
1172             ('uintmax_t', 'HAVE_STDINT_H_WITH_UINTMAX', '#include <stdint.h>'),
1173         ],
1174         libs = [
1175             ('c', 'HAVE_LIBC'),
1176         ],
1177         custom_tests = [
1178             (conf.CheckLC_MESSAGES(),
1179                 'HAVE_LC_MESSAGES',
1180                 'Define if your <locale.h> file defines LC_MESSAGES.'
1181             ),
1182             (conf.CheckIconvConst(),
1183                 'ICONV_CONST',
1184                 'Define as const if the declaration of iconv() needs const.',
1185                 '#define ICONV_CONST const',
1186                 '#define ICONV_CONST',
1187             ),
1188             (conf.CheckType('intmax_t', includes='#include <stdint.h>') or \
1189             conf.CheckType('intmax_t', includes='#include <inttypes.h>'),
1190                 'HAVE_INTMAX_T',
1191                 "Define to 1 if you have the `intmax_t' type."
1192             ),
1193             (env.has_key('nls') and env['nls'],
1194                 'ENABLE_NLS',
1195                 "Define to 1 if translation of program messages to the user's native anguage is requested.",
1196             ),
1197         ],
1198         extra_items = [
1199             ('#define HAVE_ICONV 1', 'Define if iconv or libiconv is found'),
1200             ('#define SIZEOF_WCHAR_T %d' % sizeof_wchar_t,
1201                 'Define to be the size of type wchar_t'),
1202         ],
1203         config_post = '#endif'
1204     )
1205
1206     # these keys are needed in env
1207     for key in ['HAVE_ASPRINTF', 'HAVE_WPRINTF', 'HAVE_SNPRINTF', \
1208         'HAVE_POSIX_PRINTF', 'HAVE_LIBC']:
1209         # USE_ASPELL etc does not go through result
1210         if result.has_key(key):
1211             env[key] = result[key]
1212
1213
1214 # this looks misplaced, but intl/libintl.h is needed by src/message.C
1215 if env['nls'] and included_gettext:
1216     # libgnuintl.h.in => libintl.h
1217     env.Depends('$TOP_SRCDIR/intl/libintl.h', '$BUILDDIR/intl/config.h')
1218     env.substFile('$BUILDDIR/intl/libintl.h', '$TOP_SRCDIR/intl/libgnuintl.h.in')
1219     env.Command('$BUILDDIR/intl/libgnuintl.h', '$BUILDDIR/intl/libintl.h',
1220         [Copy('$TARGET', '$SOURCE')])
1221
1222 #
1223 # Finish auto-configuration
1224 env = conf.Finish()
1225
1226 #----------------------------------------------------------
1227 # Now set up our build process accordingly
1228 #----------------------------------------------------------
1229
1230 if env['ICONV_LIB'] is None:
1231     system_libs = []
1232 else:
1233     system_libs = [env['ICONV_LIB']]
1234 if platform_name in ['win32', 'cygwin']:
1235     # the final link step needs stdc++ to succeed under mingw
1236     # FIXME: shouldn't g++ automatically link to stdc++?
1237     if use_vc:
1238         system_libs += ['ole32', 'shlwapi', 'shell32', 'advapi32', 'zdll']
1239     else:
1240         system_libs += ['shlwapi', 'stdc++', 'z']
1241 elif platform_name == 'cygwin' and env['X11']:
1242     system_libs += ['GL',  'Xmu', 'Xi', 'Xrender', 'Xrandr',
1243         'Xcursor', 'Xft', 'freetype', 'fontconfig', 'Xext', 'X11', 'SM', 'ICE', 
1244         'resolv', 'pthread', 'z']
1245 else:
1246     system_libs += ['z']
1247
1248 libs = [
1249     ('HAVE_LIBGDI32', 'gdi32'),
1250     ('HAVE_LIBAIKSAURUS', env['AIKSAURUS_LIB']),
1251     ('USE_ASPELL', aspell_lib),
1252     ('USE_ISPELL', 'ispell'),
1253     ('USE_PSPELL', 'pspell'),
1254 ]
1255
1256 for lib in libs:
1257     if env[lib[0]]:
1258         system_libs.append(lib[1])
1259
1260 #
1261 # Build parameters CPPPATH etc
1262 #
1263 if env['X11']:
1264     env.AppendUnique(LIBPATH = ['/usr/X11R6/lib'])
1265
1266 #
1267 # boost: for boost header files
1268 # BUILDDIR/common: for config.h
1269 # TOP_SRCDIR/src: for support/* etc
1270 #
1271 env['CPPPATH'] += ['$BUILDDIR/common', '$TOP_SRCDIR/src']
1272 #
1273 # Separating boost directories from CPPPATH stops scons from building
1274 # the dependency tree for boost header files, and effectively reduce
1275 # the null build time of lyx from 29s to 16s. Since lyx may tweak local
1276 # boost headers, this is only done for system boost headers.
1277 if included_boost:
1278     env.AppendUnique(CPPPATH = ['$BOOST_INC_PATH'])
1279 else:
1280     if use_vc:
1281         env.PrependUnique(CCFLAGS = ['/I$BOOST_INC_PATH'])
1282     else:
1283         env.PrependUnique(CCFLAGS = ['-I$BOOST_INC_PATH'])
1284
1285 # for intl/config.h, intl/libintl.h and intl/libgnuintl.h
1286 if env['nls'] and included_gettext:
1287     env['CPPPATH'].append('$BUILDDIR/intl')
1288 #
1289
1290 #
1291 # A Link script for cygwin see
1292 # http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
1293 # http://www.cygwin.com/ml/cygwin-apps/2004-09/msg00309.html
1294 # for details
1295 #
1296 if platform_name == 'cygwin':
1297     ld_script_path = '/tmp'
1298     ld_script = utils.installCygwinLDScript(ld_script_path)
1299     env.AppendUnique(LINKFLAGS = ['-Wl,--enable-runtime-pseudo-reloc',
1300         '-Wl,--script,%s' % ld_script, '-Wl,-s'])
1301
1302
1303 #---------------------------------------------------------
1304 # Frontend related variables (QTDIR etc)
1305 #---------------------------------------------------------
1306
1307 #
1308 # create a separate environment so that other files do not have
1309 # to be built with all the include directories etc
1310 #
1311 if frontend == 'qt4':
1312     frontend_env = env.Copy()
1313
1314     # handle qt related user specified paths
1315     # set environment so that moc etc can be found even if its path is not set properly
1316     if frontend_env.has_key('qt_dir') and frontend_env['qt_dir']:
1317         frontend_env['QTDIR'] = frontend_env['qt_dir']
1318         if os.path.isdir(os.path.join(frontend_env['qt_dir'], 'bin')):
1319             os.environ['PATH'] += os.pathsep + os.path.join(frontend_env['qt_dir'], 'bin')
1320             frontend_env.PrependENVPath('PATH', os.path.join(frontend_env['qt_dir'], 'bin'))
1321         if os.path.isdir(os.path.join(frontend_env['qt_dir'], 'lib')):
1322             frontend_env.PrependENVPath('PKG_CONFIG_PATH', os.path.join(frontend_env['qt_dir'], 'lib'))
1323
1324     # if separate qt_lib_path is given
1325     if frontend_env.has_key('qt_lib_path') and frontend_env['qt_lib_path']:
1326         qt_lib_path = frontend_env.subst('$qt_lib_path')
1327         frontend_env.AppendUnique(LIBPATH = [qt_lib_path])
1328         frontend_env.PrependENVPath('PKG_CONFIG_PATH', qt_lib_path)
1329     else:
1330         qt_lib_path = None
1331
1332     # if separate qt_inc_path is given
1333     if frontend_env.has_key('qt_inc_path') and frontend_env['qt_inc_path']:
1334         qt_inc_path = frontend_env['qt_inc_path']
1335     else:
1336         qt_inc_path = None
1337
1338     # local qt4 toolset from
1339     # http://www.iua.upf.es/~dgarcia/Codders/sconstools.html
1340     #
1341     # NOTE: I have to patch qt4.py since it does not automatically
1342     # process .C file!!! (add to cxx_suffixes )
1343     #
1344     frontend_env.Tool('qt4', [scons_dir])
1345     frontend_env['QT_AUTOSCAN'] = 0
1346     frontend_env['QT4_AUTOSCAN'] = 0
1347     frontend_env['QT4_UICDECLFLAGS'] = '-tr lyx::qt_'
1348
1349     if qt_lib_path is None:
1350         qt_lib_path = os.path.join(frontend_env.subst('$QTDIR'), 'lib')
1351     if qt_inc_path is None:
1352         qt_inc_path = os.path.join(frontend_env.subst('$QTDIR'), 'include')
1353
1354
1355     conf = Configure(frontend_env,
1356         custom_tests = { 
1357             'CheckPackage' : utils.checkPackage,
1358             'CheckCommand' : utils.checkCommand,
1359         }
1360     )
1361
1362     succ = False
1363     # first: try pkg_config
1364     if frontend_env['HAS_PKG_CONFIG']:
1365         succ = conf.CheckPackage('QtCore') or conf.CheckPackage('QtCore4')
1366         # FIXME: use pkg_config information?
1367         #frontend_env['QT4_PKG_CONFIG'] = succ
1368     # second: try to link to it
1369     if not succ:
1370         # Under linux, I can test the following perfectly
1371         # Under windows, lib names need to passed as libXXX4.a ...
1372         if platform_name == 'win32':
1373             succ = conf.CheckLibWithHeader('QtCore4', 'QtGui/QApplication', 'c++', 'QApplication qapp();')
1374         else:
1375             succ = conf.CheckLibWithHeader('QtCore', 'QtGui/QApplication', 'c++', 'QApplication qapp();')
1376     # still can not find it
1377     if not succ:
1378         print 'Did not find qt libraries, exiting!'
1379         Exit(1)
1380     #
1381     # Now, determine the correct suffix:
1382     qt_libs = ['QtCore', 'QtGui']
1383     if platform_name == 'win32':
1384         if mode == 'debug' and use_vc and \
1385             conf.CheckLibWithHeader('QtCored4', 'QtGui/QApplication', 'c++', 'QApplication qapp();'):
1386             qt_lib_suffix = 'd4'
1387             use_qt_debug_libs = True
1388         else:
1389             qt_lib_suffix = '4'
1390             use_qt_debug_libs = False
1391     else:
1392         if mode == 'debug' and conf.CheckLibWithHeader('QtCore_debug', 'QtGui/QApplication', 'c++', 'QApplication qapp();'):
1393             qt_lib_suffix = '_debug'
1394             use_qt_debug_libs = True
1395         else:
1396             qt_lib_suffix = ''
1397             use_qt_debug_libs = False
1398     frontend_env.EnableQt4Modules(qt_libs, debug = (mode == 'debug' and use_qt_debug_libs))
1399     frontend_libs = [x + qt_lib_suffix for x in qt_libs]
1400     qtcore_lib = ['QtCore' + qt_lib_suffix]
1401
1402     # check uic and moc commands for qt frontends
1403     if conf.CheckCommand('uic') == None or conf.CheckCommand('moc') == None:
1404         print 'uic or moc command is not found for frontend', frontend
1405         Exit(1)
1406     
1407     # now, if msvc2005 is used, we will need to embed lyx.exe.manifest to lyx.exe
1408     # NOTE: previously, lyx.exe had to be linked to some qt manifest to work.
1409     # For some unknown changes in msvc or qt, this is no longer needed.
1410     if use_vc:
1411         frontend_env['LINKCOM'] = [frontend_env['LINKCOM'], \
1412             'mt.exe /MANIFEST %s /outputresource:$TARGET;1' % \
1413             env.File('$BUILDDIR/lyx.exe.manifest').path]
1414
1415     frontend_env = conf.Finish()
1416
1417 #
1418 # Report results
1419 #
1420 # fill in the version info
1421 env['VERSION_INFO'] = '''Configuration
1422   Host type:                      %s
1423   Special build flags:            %s
1424   C   Compiler:                   %s
1425   C   Compiler flags:             %s %s
1426   C++ Compiler:                   %s
1427   C++ Compiler LyX flags:         %s
1428   C++ Compiler flags:             %s %s
1429   Linker flags:                   %s
1430   Linker user flags:              %s
1431 Build info:
1432   Builing directory:              %s
1433   Local library directory:        %s
1434   Libraries paths:                %s
1435   Boost libraries:                %s
1436   Frontend libraries:             %s
1437   System libraries:               %s
1438   include search path:            %s
1439 Frontend:
1440   Frontend:                       %s
1441   Packaging:                      %s
1442   LyX dir:                        %s
1443   LyX files dir:                  %s
1444 ''' % (platform_name,
1445     env.subst('$CCFLAGS'), env.subst('$CC'),
1446     env.subst('$CPPFLAGS'), env.subst('$CFLAGS'),
1447     env.subst('$CXX'), env.subst('$CXXFLAGS'),
1448     env.subst('$CPPFLAGS'), env.subst('$CXXFLAGS'),
1449     env.subst('$LINKFLAGS'), env.subst('$LINKFLAGS'),
1450     env.subst('$BUILDDIR'), env.subst('$LOCALLIBPATH'),
1451     str(env['LIBPATH']), str(boost_libraries),
1452     str(frontend_libs), str(system_libs), str(env['CPPPATH']),
1453     frontend, packaging_method,
1454     prefix, env['LYX_DIR'])
1455
1456 if frontend in ['qt4']:
1457     env['VERSION_INFO'] += '''  include dir:                    %s
1458   library dir:                    %s
1459   X11:                            %s
1460 ''' % (qt_inc_path, qt_lib_path, env['X11'])
1461
1462 print env['VERSION_INFO']
1463
1464 #
1465 # Mingw command line may be too short for our link usage,
1466 # Here we use a trick from scons wiki
1467 # http://www.scons.org/cgi-sys/cgiwrap/scons/moin.cgi/LongCmdLinesOnWin32
1468 #
1469 # I also would like to add logging (commands only) capacity to the
1470 # spawn system.
1471 logfile = env.get('logfile', default_log_file)
1472 if logfile != '' or platform_name == 'win32':
1473     import time
1474     utils.setLoggedSpawn(env, logfile, longarg = (platform_name == 'win32'),
1475         info = '''# This is a log of commands used by scons to build lyx
1476 # Time: %s
1477 # Command: %s
1478 # Info: %s
1479 ''' % (time.asctime(), ' '.join(sys.argv),
1480     env['VERSION_INFO'].replace('\n','\n# ')) )
1481
1482
1483 # Cleanup stuff
1484 #
1485 # -h will print out help info
1486 Help(opts.GenerateHelpText(env))
1487
1488
1489
1490 #----------------------------------------------------------
1491 # Start building
1492 #----------------------------------------------------------
1493 # this has been the source of problems on some platforms...
1494 # I find that I need to supply it with full path name
1495 env.SConsignFile(os.path.join(Dir(env['BUILDDIR']).abspath, '.sconsign'))
1496 # this usage needs further investigation.
1497 #env.CacheDir('%s/Cache/%s' % (env['BUILDDIR'], frontend))
1498
1499 print "Building all targets recursively"
1500
1501 if env.has_key('rebuild'):
1502     rebuild_targets = env['rebuild'].split(',')
1503     if 'none' in rebuild_targets or 'no' in rebuild_targets:
1504         rebuild_targets = []
1505     elif 'all' in rebuild_targets or 'yes' in rebuild_targets:
1506         # None: let scons decide which components to build
1507         # Forcing all components to be rebuilt is in theory not necessary
1508         rebuild_targets = None    
1509 else:
1510     rebuild_targets = None
1511
1512 def libExists(libname):
1513     ''' Check whether or not lib $LOCALLIBNAME/libname already exists'''
1514     return os.path.isfile(File(env.subst('$LOCALLIBPATH/${LIBPREFIX}%s$LIBSUFFIX'%libname)).abspath)
1515
1516 def appExists(apppath, appname):
1517     ''' Check whether or not application already exists'''
1518     return os.path.isfile(File(env.subst('$BUILDDIR/common/%s/${PROGPREFIX}%s$PROGSUFFIX' % (apppath, appname))).abspath)
1519
1520 targets = BUILD_TARGETS
1521 build_install = 'install' in targets or 'installer' in targets
1522 build_installer = 'installer' in targets
1523 # msvc need to pass full target name, so I have to look for path/lyx etc
1524 build_lyx = build_installer or targets == [] or True in ['lyx' in x for x in targets] \
1525     or build_install or 'all' in targets
1526 build_boost = (included_boost and not libExists('boost_regex')) or 'boost' in targets
1527 build_intl = (included_gettext and not libExists('included_intl')) or 'intl' in targets
1528 build_support = build_lyx or True in [x in targets for x in ['support', 'client', 'tex2lyx']]
1529 build_mathed = build_lyx or 'mathed' in targets
1530 build_insets = build_lyx or 'insets' in targets
1531 build_frontends = build_lyx or 'frontends' in targets
1532 build_graphics = build_lyx or 'graphics' in targets
1533 build_controllers = build_lyx or 'controllers' in targets
1534 build_client = True in ['client' in x for x in targets] \
1535     or build_install or 'all' in targets or build_installer
1536 build_tex2lyx = True in ['tex2lyx' in x for x in targets] \
1537     or build_install or 'all' in targets or build_installer
1538 build_lyxbase = build_lyx or 'lyxbase' in targets
1539 update_po = 'update_po' in targets
1540 update_manifest = 'update_manifest' in targets
1541 build_po = 'po' in targets or build_install or 'all' in targets
1542 build_qt4 = (build_lyx and frontend == 'qt4') or 'qt4' in targets
1543 build_msvs_projects = use_vc and 'msvs_projects' in targets
1544
1545
1546 # now, if rebuild_targets is specified, do not rebuild some targets
1547 if rebuild_targets is not None:
1548     #
1549     def ifBuildLib(name, libname, old_value):
1550         # explicitly asked to rebuild
1551         if name in rebuild_targets:
1552             return True
1553         # else if not rebuild, and if the library already exists
1554         elif libExists(libname):
1555             return False
1556         # do not change the original value
1557         else:
1558             return old_value
1559     build_boost = ifBuildLib('boost', 'included_boost_filesystem', build_boost)
1560     build_intl = ifBuildLib('intl', 'included_intl', build_intl)
1561     build_support = ifBuildLib('support', 'support', build_support)
1562     build_mathed = ifBuildLib('mathed', 'mathed', build_mathed)
1563     build_insets = ifBuildLib('insets', 'insets', build_insets)
1564     build_frontends = ifBuildLib('frontends', 'frontends', build_frontends)
1565     build_graphics = ifBuildLib('graphics', 'graphics', build_graphics)
1566     build_controllers = ifBuildLib('controllers', 'controllers', build_controllers)
1567     build_lyxbase = ifBuildLib('lyxbase', 'lyxbase_pre', build_lyxbase)
1568     build_qt4 = ifBuildLib('qt4', 'qt4', build_qt4)
1569     #
1570     def ifBuildApp(name, appname, old_value):
1571         # explicitly asked to rebuild
1572         if name in rebuild_targets:
1573             return True
1574         # else if not rebuild, and if the library already exists
1575         elif appExists(name, appname):
1576             return False
1577         # do not change the original value
1578         else:
1579             return old_value
1580     build_tex2lyx = ifBuildApp('tex2lyx', 'tex2lyx', build_tex2lyx)
1581     build_client = ifBuildApp('client', 'lyxclient', build_client)
1582
1583 # sync frontend and frontend (?)
1584 if build_qt4:
1585     frontend = 'qt4'
1586
1587
1588 if build_boost:
1589     #
1590     # boost libraries
1591     #
1592     # special builddir
1593     env.BuildDir('$BUILDDIR/boost', '$TOP_SRCDIR/boost/libs', duplicate = 0)
1594
1595     boostenv = env.Copy()
1596     #
1597     # boost use its own config.h
1598     boostenv['CPPPATH'] = ['$TOP_SRCDIR/boost', '$BUILDDIR/boost'] + extra_inc_paths
1599     boostenv.AppendUnique(CCFLAGS = ['-DBOOST_USER_CONFIG="<config.h>"'])
1600
1601     for lib in boost_libs:
1602         print 'Processing files in boost/libs/%s/src...' % lib
1603         boostlib = boostenv.StaticLibrary(
1604             target = '$LOCALLIBPATH/included_boost_%s' % lib,
1605             source = ['$BUILDDIR/boost/%s/src/%s' % (lib, x) for x in eval('boost_libs_%s_src_files' % lib)]
1606         )
1607         Alias('boost', boostlib)
1608
1609
1610 if build_intl:
1611     #
1612     # intl
1613     #
1614     intlenv = env.Copy()
1615
1616     print "Processing files in intl..."
1617
1618     env.BuildDir('$BUILDDIR/intl', '$TOP_SRCDIR/intl', duplicate = 0)
1619
1620     # we need the original C compiler for these files
1621     intlenv['CC'] = C_COMPILER
1622     intlenv['CCFLAGS'] = C_CCFLAGS
1623     if use_vc:
1624         intlenv.Append(CCFLAGS=['/Dinline#', '/D__attribute__(x)#', '/Duintmax_t=UINT_MAX'])
1625     # intl does not use global config.h
1626     intlenv['CPPPATH'] = ['$BUILDDIR/intl'] + extra_inc_paths
1627
1628     intlenv.Append(CCFLAGS = [
1629         r'-DLOCALEDIR=\"' + env['LOCALEDIR'].replace('\\', '\\\\') + r'\"',
1630         r'-DLOCALE_ALIAS_PATH=\"' + env['LOCALEDIR'].replace('\\', '\\\\') + r'\"',
1631         r'-DLIBDIR=\"' + env['TOP_SRCDIR'].replace('\\', '\\\\') + r'/lib\"',
1632         '-DIN_LIBINTL',
1633         '-DENABLE_RELOCATABLE=1',
1634         '-DIN_LIBRARY',
1635         r'-DINSTALLDIR=\"' + prefix.replace('\\', '\\\\') + r'/lib\"',
1636         '-DNO_XMALLOC',
1637         '-Dset_relocation_prefix=libintl_set_relocation_prefix',
1638         '-Drelocate=libintl_relocate',
1639         '-DDEPENDS_ON_LIBICONV=1',
1640         '-DHAVE_CONFIG_H'
1641         ]
1642     )
1643
1644     intl = intlenv.StaticLibrary(
1645         target = '$LOCALLIBPATH/included_intl',
1646         LIBS = ['c'],
1647         source = ['$BUILDDIR/intl/%s' % x for x in intl_files]
1648     )
1649     Alias('intl', intl)
1650
1651
1652 #
1653 # Now, src code under src/
1654 #
1655 env.BuildDir('$BUILDDIR/common', '$TOP_SRCDIR/src', duplicate = 0)
1656
1657
1658 if build_support:
1659     #
1660     # src/support
1661     #
1662     print "Processing files in src/support..."
1663
1664     frontend_env.Depends('$BUILDDIR/common/support/Package.cpp', '$BUILDDIR/common/config.h')
1665     Package_cpp = env.substFile('$BUILDDIR/common/support/Package.cpp', '$TOP_SRCDIR/src/support/Package.cpp.in')
1666
1667     support = frontend_env.StaticLibrary(
1668         target = '$LOCALLIBPATH/support',
1669         source = ['$BUILDDIR/common/support/%s' % x for x in src_support_files] + Package_cpp,
1670     )
1671     Alias('support', support)
1672
1673
1674 if build_mathed:
1675     #
1676     # src/mathed
1677     #
1678     print "Processing files in src/mathed..."
1679     #
1680     mathed = env.StaticLibrary(
1681         target = '$LOCALLIBPATH/mathed',
1682         source = ['$BUILDDIR/common/mathed/%s' % x for x in src_mathed_files]
1683     )
1684     Alias('mathed', mathed)
1685
1686
1687 if build_insets:
1688     #
1689     # src/insets
1690     #
1691     print "Processing files in src/insets..."
1692     #
1693     insets = env.StaticLibrary(
1694         target = '$LOCALLIBPATH/insets',
1695         source = ['$BUILDDIR/common/insets/%s' % x for x in src_insets_files]
1696     )
1697     Alias('insets', insets)
1698
1699
1700 if build_frontends:
1701     #
1702     # src/frontends
1703     #
1704     print "Processing files in src/frontends..."
1705
1706     frontends = env.StaticLibrary(
1707         target = '$LOCALLIBPATH/frontends',
1708         source = ['$BUILDDIR/common/frontends/%s' % x for x in src_frontends_files]
1709     )
1710     Alias('frontends', frontends)
1711
1712
1713 if build_graphics:
1714     #
1715     # src/graphics
1716     #
1717     print "Processing files in src/graphics..."
1718
1719     graphics = env.StaticLibrary(
1720         target = '$LOCALLIBPATH/graphics',
1721         source = ['$BUILDDIR/common/graphics/%s' % x for x in src_graphics_files]
1722     )
1723     Alias('graphics', graphics)
1724
1725
1726 if build_controllers:
1727     #
1728     # src/frontends/controllers
1729     #
1730     print "Processing files in src/frontends/controllers..."
1731
1732     controllers = env.StaticLibrary(
1733         target = '$LOCALLIBPATH/controllers',
1734         source = ['$BUILDDIR/common/frontends/controllers/%s' % x for x in src_frontends_controllers_files]
1735     )
1736     Alias('controllers', controllers)
1737
1738
1739 #
1740 # src/frontend/qt4
1741 #
1742 if build_qt4:
1743     env.BuildDir('$BUILDDIR/$frontend', '$TOP_SRCDIR/src/frontend/$frontend', duplicate = 0)
1744
1745     print "Processing files in src/frontends/qt4..."
1746
1747     qt4_moc_files = ["$BUILDDIR/common/frontends/qt4/%s" % x for x in src_frontends_qt4_moc_files]
1748
1749     #
1750     # Compile resources
1751     #
1752     resources = [frontend_env.Uic4(x.split('.')[0]) for x in \
1753         ["$BUILDDIR/common/frontends/qt4/ui/%s" % x for x in src_frontends_qt4_ui_files]]
1754
1755     #
1756     # moc qt4_moc_files, the moced files are included in the original files
1757     #
1758     qt4_moced_files = [frontend_env.Moc4(x.replace('.cpp', '_moc.cpp'), x.replace('.cpp', '.h')) for x in qt4_moc_files]
1759
1760     qt4 = frontend_env.StaticLibrary(
1761         target = '$LOCALLIBPATH/qt4',
1762         source = ['$BUILDDIR/common/frontends/qt4/%s' % x for x in src_frontends_qt4_files],
1763         CPPPATH = [
1764             '$CPPPATH',
1765             '$BUILDDIR/common',
1766             '$BUILDDIR/common/images',
1767             '$BUILDDIR/common/frontends',
1768             '$BUILDDIR/common/frontends/qt4',
1769             '$BUILDDIR/common/frontends/controllers'
1770         ],
1771         CCFLAGS =  [
1772             '$CCFLAGS',
1773             '-DHAVE_CONFIG_H',
1774             '-DQT_CLEAN_NAMESPACE',
1775             '-DQT_GENUINE_STR',
1776             '-DQT_NO_STL',
1777             '-DQT_NO_KEYWORDS',
1778         ]
1779     )
1780     Alias('qt4', qt4)
1781
1782
1783 if build_client:
1784     #
1785     # src/client
1786     #
1787     frontend_env.BuildDir('$BUILDDIR/common', '$TOP_SRCDIR/src', duplicate = 0)
1788
1789     print "Processing files in src/client..."
1790
1791     if env['HAVE_FCNTL']:
1792         client = frontend_env.Program(
1793             target = '$BUILDDIR/common/client/lyxclient',
1794             LIBS = ['support'] + intl_libs + system_libs +
1795                 socket_libs + boost_libraries + qtcore_lib,
1796             source = ['$BUILDDIR/common/client/%s' % x for x in src_client_files] + \
1797                 utils.createResFromIcon(frontend_env, 'lyx_32x32.ico', '$LOCALLIBPATH/client.rc')
1798         )
1799         Alias('client', frontend_env.Command(os.path.join('$BUILDDIR', os.path.split(str(client[0]))[1]),
1800             client, [Copy('$TARGET', '$SOURCE')]))
1801     else:
1802         client = None
1803     Alias('client', client)
1804 else:
1805     if env['HAVE_FCNTL']:
1806         # define client even if lyxclient is not built with rebuild=no
1807         client = [env.subst('$BUILDDIR/common/client/${PROGPREFIX}lyxclient$PROGSUFFIX')]
1808     else:
1809         client = None
1810
1811
1812 if build_tex2lyx:
1813     #
1814     # tex2lyx
1815     #
1816     print "Processing files in src/tex2lyx..."
1817
1818     #
1819     for file in src_tex2lyx_copied_files + src_tex2lyx_copied_header_files:
1820         frontend_env.Command('$BUILDDIR/common/tex2lyx/'+file, '$TOP_SRCDIR/src/'+file,
1821             [Copy('$TARGET', '$SOURCE')])
1822
1823     tex2lyx = frontend_env.Program(
1824         target = '$BUILDDIR/common/tex2lyx/tex2lyx',
1825         LIBS = ['support'] + boost_libraries + intl_libs + system_libs + qtcore_lib,
1826         source = ['$BUILDDIR/common/tex2lyx/%s' % x for x in src_tex2lyx_files + src_tex2lyx_copied_files] + \
1827             utils.createResFromIcon(frontend_env, 'lyx_32x32.ico', '$LOCALLIBPATH/tex2lyx.rc'),
1828         CPPPATH = ['$BUILDDIR/common/tex2lyx', '$CPPPATH'],
1829         LIBPATH = ['#$LOCALLIBPATH', '$LIBPATH'],
1830     )
1831     Alias('tex2lyx', frontend_env.Command(os.path.join('$BUILDDIR', os.path.split(str(tex2lyx[0]))[1]),
1832         tex2lyx, [Copy('$TARGET', '$SOURCE')]))
1833     Alias('tex2lyx', tex2lyx)
1834 else:
1835     # define tex2lyx even if tex2lyx is not built with rebuild=no
1836     tex2lyx = [frontend_env.subst('$BUILDDIR/common/tex2lyx/${PROGPREFIX}tex2lyx$PROGSUFFIX')]
1837
1838
1839 if build_lyxbase:
1840     #
1841     # src/
1842     #
1843     print "Processing files in src..."
1844
1845     env.Depends('$BUILDDIR/common/version.cpp', '$BUILDDIR/common/config.h')
1846     version_cpp = env.substFile('$BUILDDIR/common/version.cpp', '$TOP_SRCDIR/src/version.cpp.in')
1847
1848     if env.has_key('USE_ASPELL') and env['USE_ASPELL']:
1849         src_post_files.append('ASpell.cpp')
1850     elif env.has_key('USE_PSPELL') and env['USE_PSPELL']:
1851         src_post_files.append('PSpell.cpp')
1852     elif env.has_key('USE_ISPELL') and env['USE_ISPELL']:
1853         src_post_files.append('ISpell.cpp')
1854
1855     # msvc requires at least one source file with main()
1856     # so I exclude main.cpp from lyxbase
1857     lyxbase_pre = env.StaticLibrary(
1858         target = '$LOCALLIBPATH/lyxbase_pre',
1859         source = ['$BUILDDIR/common/%s' % x for x in src_pre_files] + version_cpp
1860     )
1861     lyxbase_post = env.StaticLibrary(
1862         target = '$LOCALLIBPATH/lyxbase_post',
1863         source = ["$BUILDDIR/common/%s" % x for x in src_post_files]
1864     )
1865     Alias('lyxbase', lyxbase_pre)
1866     Alias('lyxbase', lyxbase_post)
1867
1868
1869 if build_lyx:
1870     #
1871     # Build lyx with given frontend
1872     #
1873     lyx = frontend_env.Program(
1874         target = '$BUILDDIR/lyx',
1875         source = ['$BUILDDIR/common/main.cpp'] + \
1876             utils.createResFromIcon(frontend_env, 'lyx_32x32.ico', '$LOCALLIBPATH/lyx.rc'),
1877         LIBS = [
1878             'lyxbase_pre',
1879             'mathed',
1880             'insets',
1881             'frontends',
1882             frontend,
1883             'controllers',
1884             'graphics',
1885             'support',
1886             'lyxbase_post',
1887             ] +
1888             boost_libraries +
1889             frontend_libs +
1890             intl_libs +
1891             socket_libs +
1892             system_libs
1893     )
1894     Alias('lyx', lyx)
1895 else:
1896     # define lyx even if lyx is not built with rebuild=no
1897     lyx = [frontend_env.subst('$BUILDDIR/${PROGPREFIX}lyx$PROGSUFFIX')]
1898
1899
1900 if build_msvs_projects:
1901     def build_project(target, full_target = None,
1902         src = [], inc = [], res = [], rebuildTargetOnly = True):
1903         ''' build mavs project files
1904             target:      alias (correspond to directory name)
1905             full_target: full path/filename of the target
1906             src:         source files
1907             inc:         include files
1908             res:         resource files
1909             rebuildTargetOnly:     whether or not only rebuild this target
1910
1911         For non-debug-able targets like static libraries, target (alias) is
1912         enough to build the target. For executable targets, msvs need to know
1913         the full path to start debug them.
1914         '''
1915         if rebuildTargetOnly:
1916             cmds = 'rebuild='+target
1917         else:
1918             cmds = ''
1919         if full_target is None:
1920             build_target = target
1921         else:
1922             build_target = full_target
1923         # project
1924         proj = env.MSVSProject(
1925             target = target + env['MSVSPROJECTSUFFIX'],
1926             # this allows easy access to header files (along with source)
1927             srcs = [env.subst(x) for x in src + inc],
1928             incs = [env.subst('$TOP_SRCDIR/src/config.h')],
1929             localincs = [env.subst(x) for x in inc],
1930             resources = [env.subst(x) for x in res],
1931             buildtarget = build_target,
1932             cmdargs = cmds,
1933             variant = 'Debug'
1934         )
1935         Alias('msvs_projects', proj)
1936     #
1937     build_project('client', src = ['$TOP_SRCDIR/src/client/%s' % x for x in src_client_files],
1938         inc = ['$TOP_SRCDIR/src/client/%s' % x for x in src_client_header_files],
1939         rebuildTargetOnly = False,
1940         full_target = File(env.subst('$BUILDDIR/common/client/lyxclient$PROGSUFFIX')).abspath)
1941     #
1942     build_project('tex2lyx', src = ['$TOP_SRCDIR/src/tex2lyx/%s' % x for x in src_tex2lyx_files],
1943         inc = ['$TOP_SRCDIR/src/tex2lyx/%s' % x for x in src_tex2lyx_header_files],
1944         rebuildTargetOnly = False,
1945         full_target = File(env.subst('$BUILDDIR/common/tex2lyx/tex2lyx$PROGSUFFIX')).abspath)
1946     #
1947     build_project('lyx', 
1948         src = ['$TOP_SRCDIR/src/%s' % x for x in src_pre_files + src_post_files + ['version.cpp']] + \
1949             ['$TOP_SRCDIR/src/support/%s' % x for x in src_support_files + ['Package.cpp'] ] + \
1950             ['$TOP_SRCDIR/src/mathed/%s' % x for x in src_mathed_files] + \
1951             ['$TOP_SRCDIR/src/insets/%s' % x for x in src_insets_files] + \
1952             ['$TOP_SRCDIR/src/frontends/%s' % x for x in src_frontends_files] + \
1953             ['$TOP_SRCDIR/src/graphics/%s' % x for x in src_graphics_files] + \
1954             ['$TOP_SRCDIR/src/frontends/controllers/%s' % x for x in src_frontends_controllers_files] + \
1955             ['$TOP_SRCDIR/src/frontends/qt4/%s' % x for x in src_frontends_qt4_files + src_frontends_qt4_moc_files],
1956         inc = ['$TOP_SRCDIR/src/%s' % x for x in src_header_files] + \
1957             ['$TOP_SRCDIR/src/support/%s' % x for x in src_support_header_files] + \
1958             ['$TOP_SRCDIR/src/mathed/%s' % x for x in src_mathed_header_files] + \
1959             ['$TOP_SRCDIR/src/insets/%s' % x for x in src_insets_header_files] + \
1960             ['$TOP_SRCDIR/src/frontends/%s' % x for x in src_frontends_header_files] + \
1961             ['$TOP_SRCDIR/src/graphics/%s' % x for x in src_graphics_header_files] + \
1962             ['$TOP_SRCDIR/src/frontends/controllers/%s' % x for x in src_frontends_controllers_header_files] + \
1963             ['$TOP_SRCDIR/src/frontends/qt4/%s' % x for x in src_frontends_qt4_header_files],
1964         res = ['$TOP_SRCDIR/src/frontends/qt4/ui/%s' % x for x in src_frontends_qt4_ui_files],
1965         rebuildTargetOnly = False,
1966         full_target = File(env.subst('$BUILDDIR/lyx$PROGSUFFIX')).abspath)
1967
1968
1969 if update_manifest:
1970     #
1971     # update scons_manifest.py
1972     #
1973     # When you run 'scons update_manifest', it tells you which files are missing
1974     # and which files are not in the source tree. It also generates a 
1975     # scons_manifest.py.new file with all the missing files added to 
1976     # XXX_extra_files. It will *not* change other sections of existing
1977     # manifest.py
1978     #
1979     print 'Validating development/scons/scons_manifest.py...'
1980     #
1981     manifest = open(env.File('$TOP_SRCDIR/development/scons/scons_manifest.py.new').abspath, 'w')
1982     print >> manifest, 'from SCons.Util import Split\n'
1983     #
1984     ignore_dirs = ['boost/boost', 'm4', 'development', 
1985         utils.relativePath(env.Dir('$BUILDDIR').abspath, env.Dir('$TOP_SRCDIR').abspath)]
1986     ignore_types = ['.svn', '.deps', '.cache', '.tmp', '.bak', '.gmo', '.pot',
1987         '.pyc', '.pyo', '.o', '_moc.cpp', 'Makefile.in', 'config.h.in',
1988         'LaTeXConfig.lyx', 'version.cpp', 'Package.cpp']
1989     ext_types = ['_header_files', '_files', '_pre_files', '_post_files', '_moc_files', '_inc_files',
1990         '_copied_files', '_copied_header_files', '_extra_header_files', '_extra_src_files', '_extra_files']
1991     for root,path,files in os.walk(env.Dir('$TOP_SRCDIR').abspath):
1992         if os.path.split(root)[-1][0] == '.' \
1993             or True in [x in root for x in ignore_types] \
1994             or True in [utils.isSubDir(root, x) for x in ignore_dirs]:
1995             continue
1996         dirname = utils.relativePath(root, env.subst('$TOP_SRCDIR')).replace(os.sep, '_')
1997         if dirname == '':
1998             dirname = 'TOP'
1999         # files in the current manifest.py
2000         cur_files = []
2001         for ext in ext_types:
2002             if 'copied' not in ext and dirname + ext in locals():
2003                 cur_files.extend(eval(dirname + ext))
2004         cur_files.sort()
2005         # compare files with cur_files
2006         files = [x for x in files if x[0] != '.' and True not in [len(x) >= len(y) and x[-len(y):] == y for y in ignore_types]]
2007         files.sort()
2008         if cur_files != files:
2009             missing = []
2010             for f in files:
2011                 if f not in cur_files:
2012                     missing.append(f)
2013             extra = []
2014             for f in cur_files:
2015                 if f not in files:
2016                     extra.append(f)
2017             if len(missing) > 0:
2018                 print 'Missing: %s in %s' % (', '.join(missing), root)
2019                 if dirname + '_extra_files' in locals():
2020                     exec('%s_extra_files.extend(missing)' % dirname)
2021                 else:
2022                     exec('%s_extra_files = missing' % dirname)
2023             if len(extra) > 0:
2024                 print 'Extra: %s in %s' % (', '.join(extra), root)
2025         # write to a new manifest file
2026         for ext in ext_types:
2027             if dirname + ext in locals():
2028                 exec('%s%s.sort()' % (dirname, ext))
2029                 print >> manifest, "%s%s = Split('''\n   " % (dirname, ext),
2030                 print >> manifest, eval(r"'\n    '.join(%s%s)" % (dirname, ext))
2031                 print >> manifest, "''')\n\n"
2032     manifest.close()
2033     Alias('update_manifest', None)
2034
2035
2036 if update_po:
2037     #
2038     # update po files
2039     #
2040     print 'Updating po/*.po files...'
2041
2042     # whether or not update po files
2043     if not env['XGETTEXT'] or not env['MSGMERGE'] or not env['MSGUNIQ']:
2044         print 'xgettext or msgmerge does not exist. Cannot merge po files'
2045         Exit(1)
2046     # rebuild POTFILES.in
2047     POTFILES_in = env.potfiles('$TOP_SRCDIR/po/POTFILES.in', 
2048         ['$TOP_SRCDIR/src/%s' % x for x in  src_header_files + src_pre_files + src_post_files + \
2049             src_extra_src_files] + \
2050         ['$TOP_SRCDIR/src/support/%s' % x for x in src_support_header_files + src_support_files + \
2051             src_support_extra_header_files + src_support_extra_src_files] + \
2052         ['$TOP_SRCDIR/src/mathed/%s' % x for x in  src_mathed_header_files + src_mathed_files] + \
2053         ['$TOP_SRCDIR/src/insets/%s' % x for x in  src_insets_header_files + src_insets_files] + \
2054         ['$TOP_SRCDIR/src/frontends/%s' % x for x in  src_frontends_header_files + src_frontends_files] + \
2055         ['$TOP_SRCDIR/src/graphics/%s' % x for x in src_graphics_header_files + src_graphics_files] + \
2056         ['$TOP_SRCDIR/src/frontends/controllers/%s' % x for x in src_frontends_controllers_header_files + src_frontends_controllers_files] + \
2057         ['$TOP_SRCDIR/src/frontends/qt4/%s' % x for x in src_frontends_qt4_header_files + src_frontends_qt4_files + src_frontends_qt4_moc_files] + \
2058         ['$TOP_SRCDIR/src/client/%s' % x for x in src_client_header_files + src_client_files ]  + \
2059         ['$TOP_SRCDIR/src/tex2lyx/%s' % x for x in src_tex2lyx_header_files + src_tex2lyx_files ]
2060     )
2061     Alias('update_po', POTFILES_in)
2062     # build language_l10n.pot, ui_l10n.pot, layouts_l10n.pot, qt4_l10n.pot, external_l10n
2063     # and combine them to lyx.po
2064     env['LYX_POT'] = 'python $TOP_SRCDIR/po/lyx_pot.py'
2065     lyx_po = env.Command('$BUILDDIR/po/lyx.po',
2066         env.Command('$BUILDDIR/po/all.po',
2067             [env.Command('$BUILDDIR/po/qt4_l10n.pot', 
2068                 ['$TOP_SRCDIR/src/frontends/qt4/ui/%s' % x for x in src_frontends_qt4_ui_files],
2069                 '$LYX_POT -b $TOP_SRCDIR -t qt4 -o $TARGET $SOURCES'),
2070              env.Command('$BUILDDIR/po/layouts_l10n.pot', 
2071                 ['$TOP_SRCDIR/lib/layouts/%s' % x for x in lib_layouts_files + lib_layouts_inc_files],
2072                 '$LYX_POT -b $TOP_SRCDIR -t layouts -o $TARGET $SOURCES'),
2073              env.Command('$BUILDDIR/po/languages_l10n.pot', '$TOP_SRCDIR/lib/languages',
2074                 '$LYX_POT -b $TOP_SRCDIR -t languages -o $TARGET $SOURCES'),
2075              env.Command('$BUILDDIR/po/ui_l10n.pot', 
2076                 ['$TOP_SRCDIR/lib/ui/%s' % x for x in lib_ui_files],
2077                 '$LYX_POT -b $TOP_SRCDIR -t ui -o $TARGET $SOURCES'),
2078              env.Command('$BUILDDIR/po/external_l10n.pot', '$TOP_SRCDIR/lib/external_templates',
2079                 '$LYX_POT -b $TOP_SRCDIR -t external -o $TARGET $SOURCES'),
2080              ], utils.env_cat),
2081             ['$MSGUNIQ -o $TARGET $SOURCE',
2082              '''$XGETTEXT --default-domain=${TARGET.base} \
2083                 --directory=$TOP_SRCDIR --add-comments=TRANSLATORS: \
2084                 --language=C++ --join-existing \
2085                 --keyword=_ --keyword=N_ --keyword=B_ --keyword=qt_ \
2086                 --files-from=$TOP_SRCDIR/po/POTFILES.in \
2087                 --copyright-holder="LyX Developers" \
2088                 --msgid-bugs-address="lyx-devel@lists.lyx.org" ''']
2089         )
2090     env.Depends(lyx_po, POTFILES_in)
2091     # copy lyx.po to lyx.pot
2092     lyx_pot = env.Command('$BUILDDIR/po/lyx.pot', lyx_po,
2093         Copy('$TARGET', '$SOURCE'))
2094     #
2095     import glob
2096     # files to translate
2097     transfiles = glob.glob(os.path.join(env.Dir('$TOP_SRCDIR/po').abspath, '*.po'))
2098     # possibly *only* handle these languages
2099     languages = None
2100     if env.has_key('languages'):
2101         languages = env.make_list(env['languages'])
2102     # merge. if I use lan.po as $TARGET, it will be removed
2103     # before it is merged. In this builder,
2104     # $BUILDDIR/po/lang.po is merged from po/lang.po and $BUILDDIR/po/lyx.pot
2105     # and is copied to po/lang.po
2106     env['BUILDERS']['msgmerge'] = Builder(action=[
2107         '$MSGMERGE $TOP_SRCDIR/po/${TARGET.filebase}.po $SOURCE -o $TARGET',
2108         Copy('$TOP_SRCDIR/po/${TARGET.filebase}.po', '$TARGET')]
2109         )
2110     # for each po file, generate pot
2111     for po_file in transfiles:
2112         # get filename
2113         fname = os.path.split(po_file)[1]
2114         # country code
2115         country = fname.split('.')[0]
2116         #
2117         if not languages or country in languages:
2118             # merge po files, the generated lan.po_new file is copied to lan.po file.
2119             po = env.msgmerge('$BUILDDIR/po/%s.po' % country, lyx_pot)
2120             env.Depends(po, POTFILES_in)
2121             Alias('update_po', po)
2122
2123
2124 if build_po:
2125     #
2126     # po/
2127     #
2128     print 'Processing files in po...'
2129
2130     import glob
2131     # handle po files
2132     #
2133     # files to translate
2134     transfiles = glob.glob(os.path.join(env.subst('$TOP_SRCDIR'), 'po', '*.po'))
2135     # possibly *only* handle these languages
2136     languages = None
2137     if env.has_key('languages'):
2138         languages = env.make_list(env['lanauges'])
2139     # use defulat msgfmt
2140     gmo_files = []
2141     if not env['MSGFMT']:
2142         print 'msgfmt does not exist. Can not process po files'
2143     else:
2144         # create a builder
2145         env['BUILDERS']['Transfiles'] = Builder(action='$MSGFMT $SOURCE -c --statistics -o $TARGET',suffix='.gmo',src_suffix='.po')
2146         #
2147         for f in transfiles:
2148             # get filename
2149             fname = os.path.split(f)[1]
2150             # country code
2151             country = fname.split('.')[0]
2152             #
2153             if not languages or country in languages:
2154                 gmo_files.extend(env.Transfiles(f))
2155
2156
2157 if build_install:
2158     #
2159     # this part is a bit messy right now. Since scons will provide
2160     # --DESTDIR option soon, at least the dest_dir handling can be 
2161     # removed later.
2162     #
2163     # how to join dest_dir and prefix
2164     def joinPaths(path1, path2):
2165         ''' join path1 and path2, do not use os.path.join because
2166             under window, c:\destdir\d:\program is invalid '''
2167         if path1 == '':
2168             return os.path.normpath(path2)
2169         # separate drive letter
2170         (drive, path) = os.path.splitdrive(os.path.normpath(path2))
2171         # ignore drive letter, so c:\destdir + c:\program = c:\destdir\program
2172         return os.path.join(os.path.normpath(path1), path[1:])
2173     #
2174     # install to dest_dir/prefix
2175     dest_dir = env.get('DESTDIR', '')
2176     dest_prefix_dir = joinPaths(dest_dir, env.Dir(prefix).abspath)
2177     # create the directory if needed
2178     if not os.path.isdir(dest_prefix_dir):
2179         try:
2180             os.makedirs(dest_prefix_dir)
2181         except:
2182             pass
2183         if not os.path.isdir(dest_prefix_dir):
2184             print 'Can not create directory', dest_prefix_dir
2185             Exit(3)
2186     #
2187     if env.has_key('exec_prefix'):
2188         bin_dest_dir = joinPaths(dest_dir, Dir(env['exec_prefix']).abspath)
2189     else:
2190         bin_dest_dir = os.path.join(dest_prefix_dir, 'bin')
2191     if add_suffix:
2192         share_dest_dir = os.path.join(dest_prefix_dir, share_dir + program_suffix)
2193     else:
2194         share_dest_dir = os.path.join(dest_prefix_dir, share_dir)
2195     man_dest_dir = os.path.join(dest_prefix_dir, man_dir)
2196     locale_dest_dir = os.path.join(dest_prefix_dir, locale_dir)
2197     env['LYX2LYX_DEST'] = os.path.join(share_dest_dir, 'lyx2lyx')
2198     #
2199     import glob
2200     #
2201     # install executables (lyxclient may be None)
2202     #
2203     if add_suffix:
2204         version_suffix = program_suffix
2205     else:
2206         version_suffix = ''
2207     #
2208     # install lyx, if in release mode, try to strip the binary
2209     if env.has_key('STRIP') and env['STRIP'] is not None and mode != 'debug':
2210         # create a builder to strip and install
2211         env['BUILDERS']['StripInstallAs'] = Builder(action='$STRIP $SOURCE -o $TARGET')
2212
2213     # install executables
2214     for (name, obj) in (('lyx', lyx), ('tex2lyx', tex2lyx), ('client', client)):
2215         if obj is None:
2216             continue
2217         target_name = os.path.split(str(obj[0]))[1].replace(name, '%s%s' % (name, version_suffix))
2218         target = os.path.join(bin_dest_dir, target_name)
2219         if env['BUILDERS'].has_key('StripInstallAs'):
2220             env.StripInstallAs(target, obj)
2221         else:
2222             env.InstallAs(target, obj)
2223         Alias('install', target)
2224
2225     # share/lyx
2226     dirs = []
2227     for (dir,files) in [
2228             ('.', lib_files),  
2229             ('bind', lib_bind_files),
2230             ('bind/de', lib_bind_de_files),
2231             ('bind/fi', lib_bind_fi_files),
2232             ('bind/pt', lib_bind_pt_files),
2233             ('bind/sv', lib_bind_sv_files),
2234             ('doc', lib_doc_files),
2235             ('doc/clipart', lib_doc_clipart_files),
2236             ('doc/cs', lib_doc_cs_files),
2237             ('doc/da', lib_doc_da_files),
2238             ('doc/de', lib_doc_de_files),
2239             ('doc/de/clipart', lib_doc_de_clipart_files),
2240             ('doc/es', lib_doc_es_files),
2241             ('doc/es/clipart', lib_doc_es_clipart_files),
2242             ('doc/eu', lib_doc_eu_files),
2243             ('doc/fr', lib_doc_fr_files),
2244             ('doc/he', lib_doc_he_files),
2245             ('doc/hu', lib_doc_hu_files),
2246             ('doc/it', lib_doc_it_files),
2247             ('doc/nl', lib_doc_nl_files),
2248             ('doc/nb', lib_doc_nb_files),
2249             ('doc/pl', lib_doc_pl_files),
2250             ('doc/pt', lib_doc_pt_files),
2251             ('doc/ro', lib_doc_ro_files),
2252             ('doc/ru', lib_doc_ru_files),
2253             ('doc/sk', lib_doc_sk_files),
2254             ('doc/sl', lib_doc_sl_files),
2255             ('doc/sv', lib_doc_sv_files),
2256             ('examples', lib_examples_files),
2257             ('examples/ca', lib_examples_ca_files),
2258             ('examples/cs', lib_examples_cs_files),
2259             ('examples/da', lib_examples_da_files),
2260             ('examples/de', lib_examples_de_files),
2261             ('examples/es', lib_examples_es_files),
2262             ('examples/eu', lib_examples_eu_files),
2263             ('examples/fa', lib_examples_fa_files),
2264             ('examples/fr', lib_examples_fr_files),
2265             ('examples/he', lib_examples_he_files),
2266             ('examples/hu', lib_examples_hu_files),
2267             ('examples/it', lib_examples_it_files),
2268             ('examples/nl', lib_examples_nl_files),
2269             ('examples/pl', lib_examples_pl_files),
2270             ('examples/pt', lib_examples_pt_files),
2271             ('examples/ru', lib_examples_ru_files),
2272             ('examples/sl', lib_examples_sl_files),
2273             ('examples/ro', lib_examples_ro_files),
2274             ('fonts', lib_fonts_files),
2275             ('images', lib_images_files),
2276             ('images/math', lib_images_math_files),
2277             ('kbd', lib_kbd_files),
2278             ('layouts', lib_layouts_files + lib_layouts_inc_files),
2279             ('lyx2lyx', lib_lyx2lyx_files),
2280             ('scripts', lib_scripts_files),
2281             ('templates', lib_templates_files),
2282             ('tex', lib_tex_files),
2283             ('ui', lib_ui_files)]:
2284         dirs.append(env.Install(os.path.join(share_dest_dir, dir),
2285             [env.subst('$TOP_SRCDIR/lib/%s/%s' % (dir, file)) for file in files]))
2286     Alias('install', dirs)
2287
2288     # subst and install lyx2lyx_version.py which is not in scons_manifest.py
2289     env.Depends(share_dest_dir + '/lyx2lyx/lyx2lyx_version.py', '$BUILDDIR/common/config.h')
2290     env.substFile(share_dest_dir + '/lyx2lyx/lyx2lyx_version.py',
2291         '$TOP_SRCDIR/lib/lyx2lyx/lyx2lyx_version.py.in')
2292     Alias('install', share_dest_dir + '/lyx2lyx/lyx2lyx_version.py')
2293     sys.path.append(share_dest_dir + '/lyx2lyx')
2294     
2295     # generate TOC files for each doc
2296     languages = depend.all_documents(env.Dir('$TOP_SRCDIR/lib/doc').abspath)
2297     tocs = []
2298     for lang in languages.keys():
2299         if os.path.isdir(os.path.join(env.Dir('$TOP_SRCDIR/lib/doc').abspath, lang)):
2300             toc = env.installTOC(os.path.join(share_dest_dir, 'doc', lang, 'TOC.lyx'),
2301                 languages[lang])
2302             tocs.append(toc)
2303             # doc_toc.build_toc needs a installed version of lyx2lyx to execute
2304             env.Depends(toc, share_dest_dir + '/lyx2lyx/lyx2lyx_version.py')
2305         else:
2306             # this is for English
2307             toc = env.installTOC(os.path.join(share_dest_dir, 'doc', 'TOC.lyx'),
2308                 languages[lang])
2309             tocs.append(toc)
2310             env.Depends(toc, share_dest_dir + '/lyx2lyx/lyx2lyx_version.py')
2311     Alias('install', tocs)
2312     
2313     if platform_name == 'cygwin':
2314         # cygwin packaging requires a file /usr/share/doc/Cygwin/foot-vendor-suffix.README
2315         Cygwin_README = os.path.join(dest_prefix_dir, 'share', 'doc', 'Cygwin', 
2316             '%s-%s.README' % (package, package_cygwin_version))
2317         env.InstallAs(Cygwin_README,
2318             os.path.join(env.subst('$TOP_SRCDIR'), 'README.cygwin'))
2319         Alias('install', Cygwin_README)
2320         # also a directory /usr/share/doc/lyx for README etc
2321         Cygwin_Doc = os.path.join(dest_prefix_dir, 'share', 'doc', package)
2322         env.Install(Cygwin_Doc, [os.path.join(env.subst('$TOP_SRCDIR'), x) for x in \
2323             ['INSTALL', 'README', 'README.Cygwin', 'RELEASE-NOTES', 'COPYING', 'ANNOUNCE']])
2324         Alias('install', Cygwin_Doc)
2325         # cygwin fonts also need to be installed
2326         Cygwin_fonts = os.path.join(share_dest_dir, 'fonts')
2327         env.Install(Cygwin_fonts, 
2328             [env.subst('$TOP_SRCDIR/development/Win32/packaging/bakoma/%s' % file) \
2329                   for file in win32_bakoma_fonts])
2330         Alias('install', Cygwin_fonts)
2331         # we also need a post installation script
2332         tmp_script = utils.installCygwinPostinstallScript('/tmp')
2333         postinstall_path = os.path.join(dest_dir, 'etc', 'postinstall')
2334         env.Install(postinstall_path, tmp_script)
2335         Alias('install', postinstall_path)
2336
2337
2338     # man
2339     env.InstallAs(os.path.join(man_dest_dir, 'lyx' + version_suffix + '.1'),
2340         env.subst('$TOP_SRCDIR/lyx.man'))
2341     env.InstallAs(os.path.join(man_dest_dir, 'tex2lyx' + version_suffix + '.1'),
2342         env.subst('$TOP_SRCDIR/src/tex2lyx/tex2lyx.man'))
2343     env.InstallAs(os.path.join(man_dest_dir, 'lyxclient' + version_suffix + '.1'),
2344         env.subst('$TOP_SRCDIR/src/client/lyxclient.man'))
2345     Alias('install', [os.path.join(man_dest_dir, x + version_suffix + '.1') for
2346         x in ['lyx', 'tex2lyx', 'lyxclient']])
2347     # locale files?
2348     # ru.gmo ==> ru/LC_MESSAGES/lyxSUFFIX.mo
2349     for gmo in gmo_files:
2350         lan = os.path.split(str(gmo))[1].split('.')[0]
2351         dest_file = os.path.join(locale_dest_dir, lan, 'LC_MESSAGES', 'lyx' + program_suffix + '.mo')
2352         env.InstallAs(dest_file, gmo)
2353         Alias('install', dest_file)
2354
2355
2356 if build_installer:
2357     #
2358     # build windows installer using NSIS
2359     #
2360     # NOTE:
2361     # There is a nsis builder on scons wiki but it does not work with
2362     # our lyx.nsi because it does not dig through all the include directives
2363     # and find the dependencies automatically. Also, it can not parse
2364     # OutFile in lyx.nsi since it is defined as SETUP_EXE which is in turn
2365     # something rely on date.
2366     # Because of this, I am doing a simple nsis builder here.
2367     if platform_name != 'win32':
2368         print 'installer target is only available for windows platform'
2369         Exit(1)
2370     if mode != 'release':
2371         print 'installer has to be built in release mode (use option mode=release)'
2372         Exit(1)
2373     installer_files = ['$TOP_SRCDIR/development/Win32/packaging/installer/%s' \
2374             % x for x in development_Win32_packaging_installer] + \
2375         ['$TOP_SRCDIR/development/Win32/packaging/installer/components/%s' \
2376             % x for x in development_Win32_packaging_installer_components] + \
2377         ['$TOP_SRCDIR/development/Win32/packaging/installer/dialogs/%s' \
2378             % x for x in development_Win32_packaging_installer_dialogs] + \
2379         ['$TOP_SRCDIR/development/Win32/packaging/installer/graphics/%s' \
2380             % x for x in development_Win32_packaging_installer_graphics] + \
2381         ['$TOP_SRCDIR/development/Win32/packaging/installer/include/%s' \
2382             % x for x in development_Win32_packaging_installer_include] + \
2383         ['$TOP_SRCDIR/development/Win32/packaging/installer/lang/%s' \
2384             % x for x in development_Win32_packaging_installer_lang]
2385     if env.has_key('NSIS') and env['NSIS'] is not None:
2386         # create a builder to strip and install
2387         env['BUILDERS']['installer'] = Builder(generator=utils.env_nsis)
2388     else:
2389         print 'No nsis compiler is found. Existing...'
2390         Exit(2)
2391     if not env.has_key('win_installer') or env['win_installer'] is None:
2392         if devel_version:
2393             env['win_installer'] = '%s-%s-%s-Installer.exe' % (package_name, package_version, time.strftime('%Y-%m-%d'))
2394         else:
2395             env['win_installer'] = '%s-%s-Installer.exe' % (package_name, package_version)
2396     # provide default setting            
2397     if not env.has_key('deps_dir') or env['deps_dir'] is None:
2398         env['deps_dir'] = os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'lyx-windows-deps-msvc-qt4')
2399     if not os.path.isdir(env.Dir('$deps_dir').abspath):
2400         print 'Development dependency package is not found.'
2401         Exit(1)    
2402     else:
2403         env['deps_dir'] = env.Dir('$deps_dir').abspath
2404     # build bundle?
2405     if env.has_key('bundle_dir') and os.path.isdir(env.Dir('$bundle_dir').abspath):
2406         env['bundle_dir'] = env.Dir('$bundle_dir').abspath
2407     elif os.path.isdir(os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'lyx-windows-bundle-deps')):
2408         env['bundle_dir'] = os.path.join(env.Dir('$TOP_SRCDIR').abspath, 'lyx-windows-bundle-deps')
2409     else:
2410         env['bundle_dir'] = None
2411     # if absolute path is given, use it, otherwise, write to current directory
2412     if not (':' in env['win_installer'] or '/' in env['win_installer'] or '\\' in env['win_installer']):
2413         env['win_installer'] = os.path.join(env.Dir('$BUILDDIR').abspath, env['win_installer'])
2414     env.Append(NSISDEFINES={
2415         'ExeFile':env['win_installer'],
2416         'BundleExeFile':env['win_installer'].replace('.exe', '-bundle.exe'),
2417         'FilesLyx':env.Dir(dest_prefix_dir).abspath,
2418         'FilesDeps':env['deps_dir'],
2419         'FilesBundle':env['bundle_dir'],
2420         })
2421     installer = env.installer(env['win_installer'],
2422         '$TOP_SRCDIR/development/Win32/packaging/installer/lyx.nsi')
2423     # since I can not use a scanner, explicit dependent is required
2424     env.Depends(installer, 'install')
2425     env.Depends(installer, installer_files)
2426     env.Alias('installer', installer)
2427     # also generate bundle?
2428     if env.has_key('bundle') and env['bundle']:
2429         if env['bundle_dir'] is None or not os.path.isdir(env['bundle_dir']):
2430             print 'Bundle directory does not exist (default to %s\lyx-windows-bundle-deps.' % env.Dir('$TOP_SRCDIR').abspath
2431             print 'Use bundle_dir option to specify'
2432             Exit(1)
2433         # generator of the builder will add bundle stuff depending on output name
2434         bundle_installer = env.installer(env['win_installer'].replace('.exe', '-bundle.exe'),
2435             '$TOP_SRCDIR/development/Win32/packaging/installer/lyx.nsi')
2436         env.Depends(bundle_installer, 'install')
2437         env.Depends(bundle_installer, installer_files)
2438         env.Alias('installer', bundle_installer)
2439
2440 Default('lyx')
2441 Alias('all', ['lyx', 'client', 'tex2lyx'])