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