]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* lily/pango-font.cc: backport: pango-font.cc, completely. This
[lilypond.git] / SConstruct
1 # -*-python-*-
2
3 '''
4 Experimental scons (www.scons.org) building.
5
6 Usage
7
8     scons TARGET
9
10 build from source directory ./TARGET (not recursive)
11
12 Configure, build
13
14     scons [config]             # configure
15     scons                      # build all
16
17 Run from build tree
18
19     run=$(pwd)/out-scons/usr
20     export LOCALE=$run/share/locale
21     export TEXMF='{'$run/share/lilypond,$(kpsexpand '$TEXMF')'}'
22     PATH=$run/bin:$PATH
23
24     #optionally, if you do not use custom.py below
25     #export LILYPONDPREFIX=$run/share/lilypond/<VERSION>
26
27     lilypond input/simple
28
29 Other targets
30     scons mf-essential         # build minimal mf stuff
31
32     scons doc                  # build web doc
33     scons config               # reconfigure
34     scons install              # install
35     scons -c                   # clean
36     scons -h                   # help
37
38     scons /                    # build *everything* (including installation)
39
40 Options  (see scons -h)
41     scons build=DIR            # clean srcdir build, output below DIR
42     scons out=DIR              # write output for alterative config to DIR
43
44 Debugging
45     scons --debug=dtree
46     scons --debug=explain
47     scons verbose=1
48
49 Optional custom.py
50
51 import os
52 out='out-scons'
53 optimising=0
54 debugging=1
55 gui=1
56 os.path.join (os.getcwd (), '=install')
57 prefix=os.path.join (os.environ['HOME'], 'usr', 'pkg', 'lilypond')
58
59 '''
60
61
62 # TODO:
63
64 #  * reality check:
65 #     - too many stages in Environments setup
66 #       (see also buildscripts/builders.py)
67 #     - Home-brew scons.cach configuration caching
68 #     - Home-brew source tarball generating -- [why] isn't that in SCons?
69
70 #  * usability and documentation for "./configure; make" users
71
72 #  * too much cruft in toplevel SConstruct
73
74 #  * (optional) operation without CVS directories, from tarball
75
76 #  * more program configure tests, actually use full executable name
77
78 #  * install doc
79
80 #  * split doc target: doc input examples mutopia?
81
82 #  * grep FIXME $(find . -name 'S*t')
83
84 #  * drop "fast"
85
86 import re
87 import glob
88 import os
89 import string
90 import sys
91 import stat
92 import shutil
93
94 # duh, we need 0.95.1
95 EnsureSConsVersion (0, 95)
96
97 usage = r'''Usage:
98 [ENVVAR=VALUE]... scons [OPTION=VALUE]... [TARGET|DIR]...
99
100 TARGETS: clean, config, doc, dist, install, mf-essential, po-update,
101          realclean, release, sconsclean, tar, TAGS
102
103 ENVVARS: BASH, CCFLAGS, CC, CXX, LIBS, PYTHON, SH...
104          (see SConstruct:config_vars)
105
106 OPTIONS:
107 '''
108       
109
110 config_cache = 'scons.cache'
111 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
112         os.unlink (config_cache)
113
114 # All config_vars can be set as ENVVAR, eg:
115 #
116 #    CXX=g++-4.0 GS=~/usr/pkg/gs/bin/gs scons config
117 #
118 # append test_program variables automagically?
119 config_vars = [
120         'BASH',
121         'BYTEORDER',
122         'CC',
123         'CCFLAGS',
124         'CPPPATH',
125         'CPPDEFINES',
126         'CXX',
127         'CXXFLAGS',
128         'DEFINES',
129         'DVIPS',
130         'FONTFORGE',
131         'GCC',
132         'GXX',
133         'GS',
134         'LIBS',
135         'LINKFLAGS',
136         'MF',
137         'MFTRACE',
138         'PERL',
139         'PYTHON',
140         'SH',
141         ]
142
143 # Put your favourite stuff in custom.py
144 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
145 opts.Add ('prefix', 'Install prefix', '/usr/')
146 opts.Add ('out', 'Output directory', 'out-scons')
147 opts.Add ('build', 'Build directory', '.')
148 opts.Add ('DESTDIR', 'DESTDIR prepended to prefix', '')
149 opts.AddOptions (
150         BoolOption ('warnings', 'compile with -Wall and similiar',
151                    1),
152         BoolOption ('debugging', 'compile with debugging symbols',
153                     0),
154         BoolOption ('optimising', 'compile with optimising',
155                     1),
156         BoolOption ('shared', 'build shared libraries',
157                     0),
158         BoolOption ('static', 'build static libraries',
159                     1),
160         BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
161                     1),
162         BoolOption ('verbose', 'run commands with verbose flag',
163                     0),
164         BoolOption ('checksums', 'use checksums instead of timestamps',
165                     0),
166         BoolOption ('fast', 'use timestamps, implicit cache, prune CPPPATH',
167                     0),
168         )
169
170 srcdir = Dir ('.').srcnode ().abspath
171 #ugh
172 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
173 import packagepython
174 package = packagepython.Package (srcdir)
175 version = packagepython.version_tuple_to_str (package.version)
176
177 ENV = { 'PATH' : os.environ['PATH'] }
178 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH', 'TEXMF']:
179         if os.environ.has_key (key):
180                 ENV[key] = os.environ[key]
181
182 env = Environment (
183         ENV = ENV,
184         BYTEORDER = sys.byteorder.upper (),
185         CC = '$GCC',
186         CXX = '$GXX',
187         CPPDEFINES = '-DHAVE_CONFIG_H',
188         MAKEINFO = 'LANG= makeinfo',
189         MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
190         
191         PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
192                                          'usr/pkg/gnome/lib'),
193                            os.path.join (os.environ['HOME'],
194                                          'usr/pkg/pango/lib')],
195         GZIP='-9v',
196         MFMODE = 'ljfour',
197         TOPLEVEL_VERSION = version,
198         )
199
200 Help (usage + opts.GenerateHelpText (env))
201
202 # Add all config_vars to opts, so that they will be read and saved
203 # together with the other configure options.
204 map (lambda x: opts.AddOptions ((x,)), config_vars)
205 opts.Update (env)
206
207 for key in config_vars:
208         if os.environ.has_key (key):
209                 env[key] = os.environ[key]
210
211 if env['fast']:
212         # Usability switch (Anthony Roach).
213         # See http://www.scons.org/cgi-bin/wiki/GoFastButton
214         # First do: scons realclean .
215         env['checksums'] = 0
216         SetOption ('max_drift', 1)
217         SetOption ('implicit_cache', 1)
218 elif env['checksums']:
219         # Always use checksums (makes more sense than timestamps).
220         SetOption ('max_drift', 0)
221         # Using *content* checksums prevents rebuilds after
222         # [re]configure if config.hh has not changed.  Too bad that it
223         # is unusably slow.
224         TargetSignatures ('content')
225
226 absbuild = Dir (env['build']).abspath
227 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
228 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
229
230
231 config_hh = os.path.join (outdir, 'config.hh')
232 version_hh = os.path.join (outdir, 'version.hh')
233
234 env.Alias ('config', config_cache)
235
236 cachedir = os.path.join (outdir, 'build-cache')
237
238 if not os.path.exists (cachedir):
239         os.makedirs (cachedir)
240
241 CacheDir (cachedir)
242
243 # No need to set $LILYPONDPREFIX to run lily, but cannot install...
244 if env['debugging'] and not 'install' in COMMAND_LINE_TARGETS:
245         env['prefix'] = run_prefix
246
247 prefix = env['prefix']
248 bindir = os.path.join (prefix, 'bin')
249 sharedir = os.path.join (prefix, 'share')
250 libdir = os.path.join (prefix, 'lib')
251 libdir_package = os.path.join (libdir, package.name)
252 lidbir_package_version = os.path.join (libdir_package, version)
253 localedir = os.path.join (sharedir, 'locale')
254 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
255 sharedir_package = os.path.join (sharedir, package.name)
256 sharedir_package_version = os.path.join (sharedir_package, version)
257 lilypondprefix = sharedir_package_version
258
259 # junkme
260 env.Append (
261         absbuild = absbuild,
262         srcdir = srcdir,
263         )
264
265
266 def list_sort (lst):
267         sorted = lst
268         sorted.sort ()
269         return sorted
270
271
272 def configure (target, source, env):
273         vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9]([.0-9]*[0-9])*).*$',
274                           re.DOTALL)
275         def get_version (program):
276                 command = '(pkg-config --modversion %(program)s || %(program)s --version || %(program)s -V) 2>&1' % vars ()
277                 pipe = os.popen (command)
278                 output = pipe.read ()
279                 if pipe.close ():
280                         return None
281                 v = re.sub (vre, '\\1', output)
282                 if v[-1] == '\n':
283                         v = v[:-1]
284                 return string.split (v, '.')
285
286         def test_version (lst, full_name, minimal, description, package):
287                 program = os.path.basename (full_name)
288                 sys.stdout.write ('Checking %s version... ' % program)
289                 actual = get_version (program)
290                 if not actual:
291                         print 'not found'
292                         lst.append ((description, package, minimal, program,
293                                      'not installed'))
294                         return 0
295                 print string.join (actual, '.')
296                 if map (string.atoi, actual) \
297                    < map (string.atoi, string.split (minimal, '.')):
298                         lst.append ((description, package, minimal, program,
299                                      string.join (actual, '.')))
300                         return 0
301                 return 1
302
303         def test_program (lst, program, minimal, description, package):
304                 key = program.upper ()
305                 if key.find ('+-'):
306                         key = re.sub ('\+', 'X', key)
307                         key = re.sub ('-', '_', key)
308                 sys.stdout.write ('Checking for %s ... ' % program)
309                 if env.has_key (key):
310                         f = env[key]
311                         sys.stdout.write ('(cached) ')
312                 else:
313                         f = WhereIs (program)
314                         env[key] = f
315                 if not f:
316                         print 'not found'
317                         lst.append ((description, package, minimal, program,
318                                      'not installed'))
319                         return 0
320                 print f
321                 return test_version (lst, program, minimal, description, package)
322
323         def test_lib (lst, program, minimal, description, package):
324                 # FIXME: test for Debian or RPM (or -foo?) based dists
325                 # to guess (or get correct!: apt-cache search?)
326                 # package name.
327                 #if os.system ('pkg-config --atleast-version=0 freetype2'):
328                 # barf
329                 if test_version (lst, program, minimal, description,
330                                  'lib%(package)s-dev or %(package)s-devel'
331                                  % vars ()):
332                         env.ParseConfig ('pkg-config --cflags --libs %(program)s'
333                                          % vars ())
334                         return 1
335                 return 0
336
337         required = []
338         test_program (required, 'bash', '2.0', 'Bash', 'bash')
339         test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
340         test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
341         test_program (required, 'guile-config', '1.6', 'GUILE development',
342                         'libguile-dev or guile-devel')
343         test_program (required, 'mf', '0.0', 'Metafont', 'tetex-bin')
344         test_program (required, 'mftrace', '1.1.9',
345                       'mftrace (http://xs4all.nl/~hanwen/mftrace)', 'mftrace')
346         test_program (required, 'potrace', '0.0', 'Potrace', 'potrace')
347         test_program (required, 'python', '2.1', 'Python (www.python.org)',
348                       'python')
349         test_program (required, 'sh', '0.0', 'Bourne shell', 'sh')
350
351         optional = []
352         # Do not use bison 1.50 and 1.75.
353         #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
354         test_program (optional, 'bison', '1.25', 'Bison -- parser generator',
355                         'bison')
356         test_program (optional, 'dvips', '0.0', 'Dvips', 'tetex-bin')
357         test_program (optional, 'fontforge', '0.0.20041224', 'FontForge',
358                       'fontforge')
359         test_program (optional, 'flex', '0.0', 'Flex -- lexer generator',
360                       'flex')
361         test_program (optional, 'guile', '1.6', 'GUILE scheme', 'guile')
362         test_program (optional, 'gs', '8.14',
363                       'Ghostscript PostScript interpreter',
364                       'gs or gs-afpl or gs-esp or gs-gpl')
365         test_program (optional, 'mftrace', '1.1.0', 'Metafont tracing Type1',
366                         'mftrace')
367         test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
368         test_program (optional, 'perl', '4.0',
369                       'Perl practical efficient readonly language', 'perl')
370         #test_program (optional, 'ps2pdf', '0.0', 'Ps2pdf', 'gs')
371
372         def CheckYYCurrentBuffer (context):
373                 context.Message ('Checking for yy_current_buffer... ')
374                 ret = conf.TryLink ("""using namespace std;
375                 #include <FlexLexer.h>
376                 class yy_flex_lexer: public yyFlexLexer
377                 {
378                 public:
379                 yy_flex_lexer ()
380                 {
381                 yy_current_buffer = 0;
382                 }
383                 };""", '.cc')
384                 context.Result (ret)
385                 return ret
386
387         def CheckLibkpathseaSo (context):
388                 saveCFLAGS = []
389                 if context.env.has_key ('CFLAGS'):
390                         saveCFLAGS = context.env['CFLAGS']
391                 CFLAGS_shared_no_debugging = filter (lambda x: x != '-g',
392                                                      saveCFLAGS)\
393                                                      + ['-shared']
394                 # FIXME: how does this work, with scons
395                 context.env.Replace (CFLAGS = CFLAGS_shared_no_debugging)
396                 #context.env.Replace (CFLAGS = '')
397                 #context.env.Append (CFLAGS = ['-shared'])
398                 context.Message ('Checking for libkpathsea... ')
399                 ret = conf.TryLink ('''#include <kpathsea/kpathsea.h>
400                 int main ()
401                 {
402                 kpse_var_expand ("\$TEXMF");
403                 return 0;
404                 }
405                 ''', '.c')
406                 context.env.Replace (CFLAGS = saveCFLAGS)
407                 # FIXME: this prints 'ok' already
408                 context.Result (ret)
409                 if not ret:
410                         return 0
411                 
412                 sys.stdout.write ('Checking for libkpathsea.so... ')
413                 testfile = str (context.sconf.lastTarget)
414                 shared_size = os.path.getsize (testfile)
415                 ret = shared_size < 40000
416                 if ret:
417                         print 'ok'
418                 else:
419                         print 'no'
420                 return ret
421
422         conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
423                                                 : CheckYYCurrentBuffer,
424                                                 'CheckLibkpathseaSo'
425                                                 : CheckLibkpathseaSo })
426
427         defines = {
428            'DIRSEP' : "'%s'" % os.sep,
429            'PATHSEP' : "'%s'" % os.pathsep,
430            'PACKAGE': '"%s"' % package.name,
431            'DATADIR' : '"%s"' % sharedir,
432            'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
433            'LOCALEDIR' : '"%s"' %localedir,
434         }
435         conf.env.Append (DEFINES = defines)
436
437         command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
438         PYTHON_INCLUDE = os.popen (command).read ()#[:-1]
439         if env['fast']:
440                 env.Append (CCFLAGS = ['-I%s' % PYTHON_INCLUDE])
441         else:
442                 env.Append (CPPPATH = [PYTHON_INCLUDE])
443
444         headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'libio.h',
445                    'Python.h')
446         for i in headers:
447                 if conf.CheckCHeader (i):
448                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
449                         conf.env['DEFINES'][key] = 1
450
451         ccheaders = ('sstream',)
452         for i in ccheaders:
453                 if conf.CheckCXXHeader (i):
454                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
455                         conf.env['DEFINES'][key] = 1
456
457         functions = ('fopencookie', 'funopen',
458                      'gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
459         for i in functions:
460                 if 0 or conf.CheckFunc (i):
461                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
462                         conf.env['DEFINES'][key] = 1
463
464         if conf.CheckYYCurrentBuffer ():
465                 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
466
467         if conf.CheckLibkpathseaSo ():
468                 conf.env['DEFINES']['HAVE_LIBKPATHSEA_SO'] = '1'
469
470         if conf.CheckLib ('dl'):
471                 pass
472
473         if conf.CheckLib ('kpathsea'):
474                 conf.env['DEFINES']['KPATHSEA'] = 1
475
476         # huh? 
477         if conf.CheckLib ('kpathsea', 'kpse_find_file'):
478                 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
479         if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
480                 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
481
482         if env['fast']:
483                 cpppath = []
484                 if env.has_key ('CPPPATH'):
485                         cpppath = env['CPPPATH']
486
487         ## FIXME: linkage, check for libguile.h and scm_boot_guile
488         #this could happen after flower...
489         env.ParseConfig ('guile-config compile')
490
491         test_program (required, 'pkg-config', '0.9.0',
492                       'pkg-config library compile manager', 'pkg-config')
493         if test_lib (required, 'freetype2', '0.0',
494                      'Development files for FreeType 2 font engine',
495                      'freetype6'):
496                 conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
497                 
498         if test_lib (required, 'pangoft2', '1.6.0',
499                      'Development files for pango, with FreeType2',
500                      'pango1.0'):
501                 conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
502                 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
503
504         if test_lib (optional, 'fontconfig', '2.2.0',
505                      'Development files for fontconfig', 'fontconfig1'):
506                 conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
507         
508         #this could happen only for compiling pango-*
509         if env['gui']:
510                 test_lib (required, 'gtk+-2.0', '2.4.0',
511                           'Development files for GTK+', 'gtk2.0')
512                 if test_lib (required, 'pango', '1.6.0',
513                           'Development files for pango', 'pango1.0'):
514                         conf.env['DEFINES']['HAVE_PANGO16'] = '1'
515                         
516                 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
517                         conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
518         if env['fast']:
519                 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
520                 # <system-dir>] speeds up SCons
521                 env['CCFLAGS'] += map (lambda x: '-I' + x,
522                                        env['CPPPATH'][len (cpppath):])
523                 env['CPPPATH'] = cpppath
524
525         if required:
526                 print
527                 print '********************************'
528                 print 'Please install required packages'
529                 for i in required:
530                         print '%s:      %s-%s or newer (found: %s %s)' % i
531                 Exit (1)
532
533         if optional:
534                 print
535                 print '*************************************'
536                 print 'Consider installing optional packages'
537                 for i in optional:
538                         print '%s:      %s-%s or newer (found: %s %s)' % i
539
540         return conf.Finish ()
541
542 def config_header (target, source, env):
543         config = open (str (target[0]), 'w')
544         for i in list_sort (env['DEFINES'].keys ()):
545                 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
546         config.close ()
547 env.Command (config_hh, config_cache, config_header)
548
549 # hmm?
550 def xuniquify (lst):
551         n = []
552         for i in lst:
553                 if not i in n:
554                         n.append (i)
555         lst = n
556         return lst
557
558 def uniquify (lst):
559         d = {}
560         n = len (lst)
561         i = 0
562         while i < n:
563                 if not d.has_key (lst[i]):
564                         d[lst[i]] = 1
565                         i += 1
566                 else:
567                         del lst[i]
568                         n -= 1
569         return lst
570
571 def uniquify_config_vars (env):
572         for i in config_vars:
573                 if env.has_key (i) and type (env[i]) == type ([]):
574                         env[i] = uniquify (env[i])
575
576 def save_config_cache (env):
577         ## FIXME: Is this smart, using option cache for saving
578         ## config.cache?  I cannot seem to find the official method.
579         uniquify_config_vars (env)
580         opts.Save (config_cache, env)
581
582         if 'config' in COMMAND_LINE_TARGETS:
583                 sys.stdout.write ('\n')
584                 sys.stdout.write ('LilyPond configured')
585                 sys.stdout.write ('\n')
586                 sys.stdout.write ('Now run')
587                 sys.stdout.write ('\n')
588                 sys.stdout.write ('    scons [TARGET|DIR]...')
589                 sys.stdout.write ('\n')
590                 sys.stdout.write ('\n')
591                 sys.stdout.write ('Examples:')
592                 sys.stdout.write ('\n')
593                 sys.stdout.write ('    scons lily    # build lilypond')
594                 sys.stdout.write ('\n')
595                 sys.stdout.write ('    scons all     # build everything')
596                 sys.stdout.write ('\n')
597                 sys.stdout.write ('    scons doc     # build documentation')
598                 sys.stdout.write ('\n')
599                 ## TODO
600                 ## sys.stdout.write ('    scons prefix=/usr DESTDIR=/tmp/pkg all install')
601                 ## sys.stdout.write ('\n')
602                 Exit (0)
603         elif not env['checksums']:
604                 # When using timestams, config.hh is NEW.  The next
605                 # build triggers recompilation of everything.  Exiting
606                 # here makes SCons use the actual timestamp for config.hh
607                 # and prevents recompiling everything the next run.
608                 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
609                 sys.stdout.write ('Running %s ... ' % command)
610                 sys.stdout.write ('\n')
611                 s = os.system (command)
612                 Exit (s)
613
614 # WTF?
615 # scons: *** Calling Configure from Builders is not supported.
616 # env.Command (config_cache, None, configure)
617 if not os.path.exists (config_cache) \
618    or (os.stat ('SConstruct')[stat.ST_MTIME]
619        > os.stat (config_cache)[stat.ST_MTIME]):
620         env = configure (None, None, env)
621         save_config_cache (env)
622 elif env['checksums']:
623         # just save everything
624         save_config_cache (env)
625
626 #urg how does #/ subst work?
627 Export ('env')
628 SConscript ('buildscripts/builder.py')
629
630 env.PrependENVPath ('PATH',
631                     os.path.join (env['absbuild'], env['out'], 'usr/bin'))
632
633 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond/', version)
634
635 env.Append (ENV = {
636         #'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond/', version),
637         'LILYPONDPREFIX' : LILYPONDPREFIX,
638         # ugh, can't use LILYPONDPREFIX here
639         #'TEXMF' : '{' + os.path.join (run_prefix, 'share/lilypond/', version)\
640         #+ ',' \
641         'TEXMF' : '{$LILYPONDPREFIX,'
642         + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
643         })
644
645 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
646 BUILD_LILYPOND = '$absbuild/lily/$out/lilypond ${__verbose}'
647 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py ${__verbose}'
648
649
650 # post-option environment-update
651 env.Append (
652         bindir = bindir,
653         sharedir = sharedir,
654         lilypond_datadir = sharedir_package,
655         localedir = localedir,
656         local_lilypond_datadir = sharedir_package_version,
657         lilypondprefix = lilypondprefix,
658         sharedir_package = sharedir_package,
659         sharedir_doc_package = sharedir_doc_package,
660         sharedir_package_version = sharedir_package_version,
661
662         # global build verbosity switch
663         __verbose = ' --verbose',
664         
665         LILYPOND = BUILD_LILYPOND,
666         ABC2LY = BUILD_ABC2LY,
667         LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
668         LILYPOND_BOOK_FORMAT = 'texi-html',
669         MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
670         # should not be necessary
671         # PYTHONPATH = ['$absbuild/python/$out'],
672         TEXI2DVI_PAPERSIZE = '@afourpaper',
673         TEXI2DVI_FLAGS = [ '-t$TEXI2DVI_PAPERSIZE'],
674         DVIPS_PAPERSIZE = 'a4',
675         DVIPS_FLAGS = ['-t$DVIPS_PAPERSIZE',
676                        '-u+lilypond.map',
677                        '-u+ec-mftrace.map'],
678         PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
679         )
680
681 if env['debugging']:
682         env.Append (CCFLAGS = ['-g', '-pipe'])
683 if env['optimising']:
684         env.Append (CCFLAGS = '-O2')
685         env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
686 if env['warnings']:
687         env.Append (CCFLAGS = ['-W', '-Wall'])
688         env.Append (CXXFLAGS = ['-Wconversion'])
689
690 # ugr,huh?
691 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
692
693 if env['verbose']:
694         env['__verbose'] = ' --verbose'
695         env['set__x'] = 'set -x;'
696
697
698 ## Explicit target and dependencies
699
700 if 'clean' in COMMAND_LINE_TARGETS:
701         # ugh: prevent reconfigure instead of clean
702         os.system ('touch %s' % config_cache)
703         
704         command = sys.argv[0] + ' -c .'
705         sys.stdout.write ('Running %s ... ' % command)
706         sys.stdout.write ('\n')
707         s = os.system (command)
708         if os.path.exists (config_cache):
709                 os.unlink (config_cache)
710         Exit (s)
711
712 if 'sconsclean' in COMMAND_LINE_TARGETS:
713         command = 'rm -rf scons.cache $(find . -name ".scon*")'
714         s = os.system (command)
715         if os.path.exists (config_cache):
716                 os.unlink (config_cache)
717         Exit (s)
718         
719 if 'realclean' in COMMAND_LINE_TARGETS:
720         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
721         sys.stdout.write ('Running %s ... ' % command)
722         sys.stdout.write ('\n')
723         s = os.system (command)
724         if os.path.exists (config_cache):
725                 os.unlink (config_cache)
726         Exit (s)
727
728 # Declare SConscript phonies 
729 env.Alias ('minimal', config_cache)
730 env.Alias ('mf-essential', config_cache)
731
732 env.Alias ('minimal', ['lily', 'mf-essential'])
733 env.Alias ('all', ['minimal', 'mf', '.'])
734 # Do we want the doc/web separation?
735 env.Alias ('doc',
736            ['Documentation',
737             'Documentation/user',
738             'Documentation/topdocs',
739             'Documentation/bibliography',
740             'input'])
741
742 # Without target arguments, do minimal build
743 if not COMMAND_LINE_TARGETS:
744         env.Default (['minimal'])
745
746 # GNU Make rerouting compat:
747 env.Alias ('web', 'doc')
748
749
750 env.Command (version_hh, '#/VERSION',
751              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
752
753 # post-config environment update
754 env.Append (
755         run_prefix = run_prefix,
756         LILYPONDPREFIX = LILYPONDPREFIX,
757
758         # FIXME: move to lily/SConscript?
759         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),
760                    os.path.join (absbuild, 'kpath-guile', env['out']),
761                    os.path.join (absbuild, 'ttftool', env['out']),],
762         CPPPATH = [outdir, ],
763         LILYPOND_PATH = ['.', '$srcdir/input',
764                          '$srcdir/input/regression',
765                          '$srcdir/input/test',
766                          '$srcdir/input/tutorial',
767                          '$srcdir/Documentation/user',
768                          '$absbuild/mf/$out',
769 #                        os.path.join (absbuild, 'Documentation',
770 #                                      env['out']),
771 #                        os.path.join (absbuild, 'Documentation/user',
772 #                                      env['out']),
773                          ],
774         MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
775                          '$absbuild/Documentation/user/$out'],
776         )
777
778 def symlink_tree (target, source, env):
779         def mkdirs (dir):
780                 def mkdir (dir):
781                         if not dir:
782                                 os.chdir (os.sep)
783                                 return
784                         if not os.path.isdir (dir):
785                                 if os.path.exists (dir):
786                                         os.unlink (dir)
787                                 os.mkdir (dir)
788                         os.chdir (dir)
789                 map (mkdir, string.split (dir, os.sep))
790         def symlink (src, dst):
791                 os.chdir (absbuild)
792                 dir = os.path.dirname (dst)
793                 mkdirs (dir)
794                 if src[0] == '#':
795                         frm = os.path.join (srcdir, src[1:])
796                 else:
797                         depth = len (string.split (dir, '/'))
798                         if src.find ('@') > -1:
799                                 frm = os.path.join ('../' * depth,
800                                                     string.replace (src, '@',
801                                                                     env['out']))
802                         else:
803                                 frm = os.path.join ('../' * depth, src,
804                                                     env['out'])
805                 if src[-1] == '/':
806                         frm = os.path.join (frm, os.path.basename (dst))
807                 if env['verbose']:
808                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
809                 os.symlink (frm, os.path.basename (dst))
810         shutil.rmtree (run_prefix)
811         prefix = os.path.join (env['out'], 'usr')
812         map (lambda x: symlink (x[0], os.path.join (prefix,
813                                                     x[1] % {'ver' : version})),
814              # ^# := source dir
815              # @  := out
816              # /$ := add dst file_name
817              (('python',     'lib/lilypond/python'),
818               # ugh
819               ('python',     'share/lilypond/%(ver)s/python'),
820               ('lily/',      'bin/lilypond'),
821               ('scripts/',   'bin/convert-ly'),
822               ('scripts/',   'bin/lilypond-book'),
823               ('scripts/',   'bin/ps2png'),
824               ('mf',         'share/lilypond/%(ver)s/dvips/mf-out'),
825               ('#ps',        'share/lilypond/%(ver)s/dvips/ps'),
826               ('#ps',        'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
827               ('mf',         'share/lilypond/%(ver)s/otf'),
828               ('mf',         'share/lilypond/%(ver)s/tfm'),
829               ('tex',        'share/lilypond/%(ver)s/tex/enc'),
830               ('#mf',        'share/lilypond/%(ver)s/fonts/mf'),
831               ('mf',         'share/lilypond/%(ver)s/fonts/map'),
832               ('mf',         'share/lilypond/%(ver)s/fonts/otf'),
833               ('mf',         'share/lilypond/%(ver)s/fonts/tfm'),
834               ('mf',         'share/lilypond/%(ver)s/fonts/type1'),
835               ('#tex',       'share/lilypond/%(ver)s/tex/source'),
836               ('tex',        'share/lilypond/%(ver)s/tex/tex-out'),
837               ('mf',         'share/lilypond/%(ver)s/tex/mf-out'),
838               ('#ly',        'share/lilypond/%(ver)s/ly'),
839               ('#scm',       'share/lilypond/%(ver)s/scm'),
840               ('#scripts',   'share/lilypond/%(ver)s/scripts'),
841               ('#ps',        'share/lilypond/%(ver)s/ps'),
842               ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
843               ('elisp',      'share/lilypond/%(ver)s/elisp')))
844
845         print "FIXME: BARF BARF BARF"
846         os.chdir (absbuild)
847         out = env['out']
848         ver = version
849         prefix = os.path.join (env['out'], 'usr/share/lilypond/%(ver)s/fonts'
850                                % vars ())
851         for ext in ('enc', 'map', 'otf', 'svg', 'tfm', 'pfa'):
852                 dir = os.path.join (absbuild, prefix, ext)
853                 os.system ('rm -f ' + dir)
854                 mkdirs (dir)
855                 os.chdir (dir)
856                 os.system ('ln -s ../../../../../../../mf/%(out)s/*.%(ext)s .'
857                            % vars ())
858         os.chdir (srcdir)
859
860 if env['debugging']:
861         stamp = os.path.join (run_prefix, 'stamp')
862         env.Command (stamp, ['#/SConstruct', '#/VERSION'],
863                      [symlink_tree, 'touch $TARGET'])
864         env.Depends ('lily', stamp)
865         
866 #### dist, tar
867 def plus (a, b):
868         a + b
869
870 def cvs_entry_is_dir (line):
871         return line[0] == 'D' and line[-2] == '/'
872
873 def cvs_entry_is_file (line):
874         return line[0] == '/' and line[-2] == '/'
875
876 def cvs_dirs (dir):
877         ENTRIES = os.path.join (dir, 'CVS/Entries')
878         if not os.path.exists (ENTRIES):
879                 return []
880         entries = open (ENTRIES).readlines ()
881         dir_entries = filter (cvs_entry_is_dir, entries)
882         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
883                     dir_entries)
884         return dirs + map (cvs_dirs, dirs)
885
886 def cvs_files (dir):
887         ENTRIES = os.path.join (dir, 'CVS/Entries')
888         entries = open (ENTRIES).readlines ()
889         file_entries = filter (cvs_entry_is_file, entries)
890         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
891         return map (lambda x: os.path.join (dir, x), files)
892
893 def flatten (tree, lst):
894         if type (tree) == type ([]):
895                 for i in tree:
896                         if type (i) == type ([]):
897                                 flatten (i, lst)
898                         else:
899                                 lst.append (i)
900         return lst
901
902 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
903         subdirs = flatten (cvs_dirs ('.'), [])
904 else:
905         # ugh
906         command = 'cd %(srcdir)s \
907         && find . -name SConscript | sed s@/SConscript@@' % vars ()
908         subdirs = string.split (os.popen (command).read ())
909
910 if env['fast']\
911    and 'all' not in COMMAND_LINE_TARGETS\
912    and 'doc' not in COMMAND_LINE_TARGETS\
913    and 'web' not in COMMAND_LINE_TARGETS\
914    and 'install' not in COMMAND_LINE_TARGETS\
915    and 'clean' not in COMMAND_LINE_TARGETS:
916         subdirs = ['lily', 'lily/include',
917                    'flower', 'flower/include',
918                    'kpath-guile',
919                    'ttftool',
920                    'mf',
921                    ]
922
923 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
924         src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
925 else:
926         src_files = ['foobar']
927
928 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
929 txt_files = map (lambda x: x + '.txt', readme_files)
930
931
932 #
933 # speeds up build by +- 5% 
934
935 if not env['fast']:
936         foo = map (lambda x: env.TXT (x + '.txt',
937                                       os.path.join ('Documentation/topdocs', x)),
938                    readme_files)
939         tar_base = package.name + '-' + version
940         tar_name = tar_base + '.tar.gz'
941         ball_prefix = os.path.join (outdir, tar_base)
942         tar_ball = os.path.join (outdir, tar_name)
943
944         dist_files = src_files + txt_files
945         ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
946         map (lambda x: env.Depends (tar_ball, x), ball_files)
947         map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
948                                     'ln $SOURCE $TARGET'), dist_files)
949         tar = env.Command (tar_ball, src_files,
950                            ['rm -f $$(find $TARGET.dir -name .sconsign)',
951                             'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
952         env.Alias ('tar', tar)
953
954         dist_ball = os.path.join (package.release_dir, tar_name)
955         env.Command (dist_ball, tar_ball,
956                      'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
957                      + 'ln $SOURCE $TARGET')
958         env.Depends ('dist', dist_ball)
959         patch_name = os.path.join (outdir, tar_base + '.diff.gz')
960         patch = env.PATCH (patch_name, tar_ball)
961         env.Depends (patch_name, dist_ball)
962         env.Alias ('release', patch)
963
964 #### web
965 if not env['fast']:
966         web_base = os.path.join (outdir, 'web')
967         web_ball = web_base + '.tar.gz'
968         env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
969         web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
970         web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
971         env['web_path'] = web_path
972         web_list = os.path.join (outdir, 'weblist')
973         # compatible make heritits
974         # fixme: generate in $outdir is cwd/builddir
975         env.Command (web_list,
976                      ## Adding 'doc' dependency is correct, but takes
977                      ## > 5min extra if you have a peder :-)
978                      #'doc',
979                      
980                      '#/VERSION',
981                      ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
982                       'cd $absbuild && $footify $$(find . -name "*.html" -print)',
983                       'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
984                       'cd $absbuild && find Documentation input $web_path \
985                       > $TARGET',
986                       '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
987                       '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
988                       'cd $absbuild && ls *.html >> $TARGET',])
989         env.Command (web_ball, web_list,
990                      ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
991         #env.Alias ('web', web_ball)
992         www_base = os.path.join (outdir, 'www')
993         www_ball = www_base + '.tar.gz'
994         env.Command (www_ball, web_ball,
995                      ['rm -rf $out/tmp',
996                       'mkdir -p $absbuild/$out/tmp',
997                       'tar -C $absbuild/$out/tmp -xzf $SOURCE',
998                       'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
999                       + ' do mv $$i $$(dirname $$i)/out-www; done',
1000                       'tar -C $absbuild/$out/tmp -czf $TARGET .'])
1001         env.Alias ('web', www_ball)
1002
1003 #### tags
1004 env.Append (
1005         ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
1006         --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
1007 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
1008 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
1009                              src_files),
1010              'etags $ETAGSFLAGS $SOURCES')
1011
1012 # Note: SConscripts are only needed in directories where something needs
1013 # to be done, building or installing
1014 for d in subdirs:
1015         if os.path.exists (os.path.join (d, 'SConscript')):
1016                 b = os.path.join (env['build'], d, env['out'])
1017                 # Support clean sourcetree build (--srcdir build)
1018                 # and ./out build.
1019                 if os.path.abspath (b) != os.path.abspath (d):
1020                         env.BuildDir (b, d, duplicate = 0)
1021                 SConscript (os.path.join (b, 'SConscript'))
1022