]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* GNUmakefile.in:
[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
26
27     lilypond-bin 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 #  * usability
64
65 #  * more program configure tests (mfont, ...?)
66
67 #  * install doc
68
69 #  * split doc target: doc input examples mutopia?
70
71 #  * grep FIXME $(find . -name 'S*t')
72
73
74 import re
75 import glob
76 import os
77 import string
78 import sys
79 import stat
80 import shutil
81
82 # duh, we need 0.95.1
83 EnsureSConsVersion (0, 95)
84
85 usage = r'''Usage:
86 scons [KEY=VALUE].. [TARGET|DIR]..
87
88 TARGETS: clean, config, doc, dist, install, mf-essential, po-update,
89          realclean, release, sconsclean, tar, TAGS
90
91 '''
92       
93
94 config_cache = 'scons.cache'
95
96 config_vars = [
97         'BASH',
98         'CCFLAGS',
99         'CPPPATH',
100         'CPPDEFINES',
101         'CXXFLAGS',
102         'DEFINES',
103         'LIBS',
104         'LINKFLAGS',
105         'METAFONT',
106         'PERL',
107         'PYTHON',
108         ]
109
110 # Put your favourite stuff in custom.py
111 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
112 opts.Add ('prefix', 'Install prefix', '/usr/')
113 opts.Add ('out', 'Output directory', 'out-scons')
114 opts.Add ('build', 'Build directory', '.')
115 opts.Add ('DESTDIR', 'DESTDIR prepended to prefix', '')
116 opts.AddOptions (
117         BoolOption ('warnings', 'compile with -Wall and similiar',
118                    1),
119         BoolOption ('debugging', 'compile with debugging symbols',
120                     0),
121         BoolOption ('optimising', 'compile with optimising',
122                     1),
123         BoolOption ('shared', 'build shared libraries',
124                     0),
125         BoolOption ('static', 'build static libraries',
126                     1),
127         BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
128                     1),
129         BoolOption ('verbose', 'run commands with verbose flag',
130                     0),
131         BoolOption ('checksums', 'use checksums instead of timestamps',
132                     0),
133         BoolOption ('fast', 'use timestamps, implicit cache, prune CPPPATH',
134                     1),
135         )
136
137 srcdir = Dir ('.').srcnode ().abspath
138 #ugh
139 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
140 import packagepython
141 package = packagepython.Package (srcdir)
142 version = packagepython.version_tuple_to_str (package.version)
143
144 ENV = { 'PATH' : os.environ['PATH'] }
145 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH']:
146         if os.environ.has_key (key):
147                 ENV[key] = os.environ[key]
148
149 env = Environment (
150         ENV = ENV,
151
152         BASH = '/bin/bash',
153         PERL = '/usr/bin/perl',
154         PYTHON = '/usr/bin/python',
155         SH = '/bin/sh',
156
157         MAKEINFO = 'LANG= makeinfo',
158         MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
159         
160         PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
161                                          'usr/pkg/gnome/lib'),
162                            os.path.join (os.environ['HOME'],
163                                          'usr/pkg/pango/lib')],
164         GZIP='-9v',
165         MFMODE = 'ljfour',
166         TOPLEVEL_VERSION = version,
167         )
168
169 # Add all config_vars to opts, so that they will be read and saved
170 # together with the other configure options.
171 map (lambda x: opts.AddOptions ((x,)), config_vars)
172
173 Help (usage + opts.GenerateHelpText (env))
174
175 opts.Update (env)
176
177 if env['fast']:
178         # Usability switch (Anthony Roach).
179         # See http://www.scons.org/cgi-bin/wiki/GoFastButton
180         # First do: scons realclean .
181         env['checksums'] = 0
182         SetOption ('max_drift', 1)
183         SetOption ('implicit_cache', 1)
184 elif env['checksums']:
185         # Always use checksums (makes more sense than timestamps).
186         SetOption ('max_drift', 0)
187         # Using *content* checksums prevents rebuilds after
188         # [re]configure if config.hh has not changed.  Too bad that it
189         # is unusably slow.
190         TargetSignatures ('content')
191
192 absbuild = Dir (env['build']).abspath
193 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
194 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
195
196
197 config_hh = os.path.join (outdir, 'config.hh')
198 version_hh = os.path.join (outdir, 'version.hh')
199
200 env.Alias ('config', config_cache)
201
202 cachedir = os.path.join (outdir, 'build-cache')
203
204 if not os.path.exists(cachedir):
205         os.makedirs(cachedir)
206
207 CacheDir (os.path.join (outdir, 'build-cache'))
208
209 # No need to set $LILYPONDPREFIX to run lily, but cannot install...
210 if env['debugging'] and not 'install' in COMMAND_LINE_TARGETS:
211         env['prefix'] = run_prefix
212
213 prefix = env['prefix']
214 bindir = os.path.join (prefix, 'bin')
215 sharedir = os.path.join (prefix, 'share')
216 libdir = os.path.join (prefix, 'lib')
217 localedir = os.path.join (sharedir, 'locale')
218 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
219 sharedir_package = os.path.join (sharedir, package.name)
220 sharedir_package_version = os.path.join (sharedir_package, version)
221 lilypondprefix = sharedir_package_version
222
223 # junkme
224 env.Append (
225         absbuild = absbuild,
226         srcdir = srcdir,
227         )
228
229
230 def list_sort (lst):
231         sorted = lst
232         sorted.sort ()
233         return sorted
234
235
236 def configure (target, source, env):
237         vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
238         def get_version (program):
239                 command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
240                 pipe = os.popen (command)
241                 output = pipe.read ()
242                 if pipe.close ():
243                         return None
244                 v = re.sub (vre, '\\1', output)
245                 return string.split (v, '.')
246
247         def test_program (lst, program, minimal, description, package):
248                 sys.stdout.write ('Checking %s version... ' % program)
249                 actual = get_version (program)
250                 if not actual:
251                         print 'not found'
252                         lst.append ((description, package, minimal, program,
253                                      'not installed'))
254                         return
255                 sys.stdout.write (string.join (actual, '.'))
256                 sys.stdout.write ('\n')
257                 if actual < string.split (minimal, '.'):
258                         lst.append ((description, package, minimal, program,
259                                      string.join (actual, '.')))
260
261         for i in ['bash', 'perl', 'python', 'sh']:
262                 sys.stdout.write ('Checking for %s... ' % i)
263                 c = WhereIs (i)
264                 key = string.upper (i)
265                 if c:
266                         env[key] = c
267                         sys.stdout.write (c)
268                 else:
269                         sys.stdout.write ('not found: %s (using: %s)' \
270                                           % (c, env[key]))
271                         # Hmm? abort?
272                 sys.stdout.write ('\n')
273
274         required = []
275         test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
276         test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
277         test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
278         test_program (required, 'guile-config', '1.6', 'GUILE development',
279                         'libguile-dev or guile-devel')
280         # Do not use bison 1.50 and 1.75.
281         test_program (required, 'bison', '1.25', 'Bison -- parser generator',
282                         'bison')
283         test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
284
285
286         optional = []
287         test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
288         test_program (optional, 'guile', '1.6', 'GUILE scheme',
289                         'libguile-dev or guile-devel')
290         test_program (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
291                         'mftrace')
292         test_program (optional, 'perl', '4.0',
293                         'Perl practical efficient readonly language', 'perl')
294         #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
295
296         def CheckYYCurrentBuffer (context):
297                 context.Message ('Checking for yy_current_buffer... ')
298                 ret = conf.TryCompile ("""using namespace std;
299                 #include <FlexLexer.h>
300                 class yy_flex_lexer: public yyFlexLexer
301                 {
302                 public:
303                 yy_flex_lexer ()
304                 {
305                 yy_current_buffer = 0;
306                 }
307                 };""", '.cc')
308                 context.Result (ret)
309                 return ret
310
311         conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
312                                                 : CheckYYCurrentBuffer })
313
314         defines = {
315            'DIRSEP' : "'%s'" % os.sep,
316            'PATHSEP' : "'%s'" % os.pathsep,
317            'TOPLEVEL_VERSION' : '"' + version + '"',
318            'PACKAGE': '"' + package.name + '"',
319            'DATADIR' : '"' + sharedir + '"',
320            'LILYPOND_DATADIR' : '"' + sharedir_package + '"',
321            'LOCAL_LILYPOND_DATADIR' : '"' + sharedir_package_version + '"',
322            'LOCALEDIR' : '"' + localedir + '"',
323         }
324         conf.env.Append (DEFINES = defines)
325
326         command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
327         PYTHON_INCLUDE = os.popen (command).read ()
328         if env['fast']:
329                 env.Append (CCFLAGS = ['-I%s ' % PYTHON_INCLUDE])
330         else:
331                 env.Append (CPPPATH = PYTHON_INCLUDE)
332
333         headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
334         for i in headers:
335                 if conf.CheckCHeader (i):
336                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
337                         conf.env['DEFINES'][key] = 1
338
339         ccheaders = ('sstream',)
340         for i in ccheaders:
341                 if conf.CheckCXXHeader (i):
342                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
343                         conf.env['DEFINES'][key] = 1
344
345         functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
346         for i in functions:
347                 if 0 or conf.CheckFunc (i):
348                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
349                         conf.env['DEFINES'][key] = 1
350
351         if conf.CheckYYCurrentBuffer ():
352                 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
353
354         if conf.CheckLib ('dl'):
355                 pass
356
357         if conf.CheckLib ('kpathsea'):
358                 conf.env['DEFINES']['KPATHSEA'] = 1
359
360         # huh? 
361         if conf.CheckLib ('kpathsea', 'kpse_find_file'):
362                 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
363         if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
364                 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
365
366         if env['fast']:
367                 cpppath = []
368                 if env.has_key ('CPPPATH'):
369                         cpppath = env['CPPPATH']
370
371         ## FIXME: linkage, check for libguile.h and scm_boot_guile
372         #this could happen after flower...
373         env.ParseConfig ('guile-config compile')
374
375         #this could happen only for compiling pango-*
376         if env['gui']:
377                 env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
378                 env.ParseConfig ('pkg-config --cflags --libs pango')
379                 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
380                         conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
381
382                 if conf.CheckLib ('pango-1.0',
383                                   'pango_fc_font_map_add_decoder_find_func'):
384                         conf.env['DEFINES']['HAVE_PANGO_CVS'] = '1'
385                         conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
386
387         if env['fast']:
388                 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
389                 # <system-dir>] speeds up SCons
390                 env['CCFLAGS'] += map (lambda x: '-I' + x,
391                                        env['CPPPATH'][len (cpppath):])
392                 env['CPPPATH'] = cpppath
393
394         if required:
395                 print
396                 print '********************************'
397                 print 'Please install required packages'
398                 for i in required:
399                         print '%s:      %s-%s or newer (found: %s %s)' % i
400                 Exit (1)
401
402         if optional:
403                 print
404                 print '*************************************'
405                 print 'Consider installing optional packages'
406                 for i in optional:
407                         print '%s:      %s-%s or newer (found: %s %s)' % i
408
409         return conf.Finish ()
410
411 def config_header (target, source, env):
412         config = open (str (target[0]), 'w')
413         for i in list_sort (env['DEFINES'].keys ()):
414                 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
415         config.close ()
416 env.Command (config_hh, config_cache, config_header)
417
418 # hmm?
419 def xuniquify (lst):
420         n = []
421         for i in lst:
422                 if not i in n:
423                         n.append (i)
424         lst = n
425         return lst
426
427 def uniquify (lst):
428         d = {}
429         n = len (lst)
430         i = 0
431         while i < n:
432                 if not d.has_key (lst[i]):
433                         d[lst[i]] = 1
434                         i += 1
435                 else:
436                         del lst[i]
437                         n -= 1
438         return lst
439
440 def uniquify_config_vars (env):
441         for i in config_vars:
442                 if env.has_key (i) and type (env[i]) == type ([]):
443                         env[i] = uniquify (env[i])
444
445 def save_config_cache (env):
446         ## FIXME: Is this smart, using option cache for saving
447         ## config.cache?  I cannot seem to find the official method.
448         uniquify_config_vars (env)
449         opts.Save (config_cache, env)
450
451         if 'config' in COMMAND_LINE_TARGETS:
452                 sys.stdout.write ('\n')
453                 sys.stdout.write ('LilyPond configured')
454                 sys.stdout.write ('\n')
455                 sys.stdout.write ('now run')
456                 sys.stdout.write ('\n')
457                 sys.stdout.write ('    scons [TARGET|DIR]...')
458                 sys.stdout.write ('\n')
459                 Exit (0)
460         elif not env['checksums']:
461                 # When using timestams, config.hh is NEW.  The next
462                 # build triggers recompilation of everything.  Exiting
463                 # here makes SCons use the actual timestamp for config.hh
464                 # and prevents recompiling everything the next run.
465                 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
466                 sys.stdout.write ('Running %s ... ' % command)
467                 sys.stdout.write ('\n')
468                 s = os.system (command)
469                 Exit (s)
470
471
472 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
473         os.unlink (config_cache)
474 # WTF?
475 # scons: *** Calling Configure from Builders is not supported.
476 # env.Command (config_cache, None, configure)
477 if not os.path.exists (config_cache) \
478    or (os.stat ('SConstruct')[stat.ST_MTIME]
479        > os.stat (config_cache)[stat.ST_MTIME]):
480         env = configure (None, None, env)
481         save_config_cache (env)
482 elif env['checksums']:
483         # just save everything
484         save_config_cache (env)
485
486 #urg how does #/ subst work?
487 Export ('env')
488 SConscript ('buildscripts/builder.py')
489
490 env.PrependENVPath ('PATH',
491                     os.path.join (env['absbuild'], env['out'], 'usr/bin'))
492
493 if os.environ.has_key ('TEXMF'):
494         env.Append (ENV = {'TEXMF' : os.environ['TEXMF']})
495 env.Append (ENV = {
496         'TEXMF' : '{$LILYPONDPREFIX,' \
497         + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
498         'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond'),
499         })
500
501 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
502 BUILD_LILYPOND = '${set__x}$PYTHON $srcdir/scripts/lilypond.py${__verbose}'
503 BUILD_LILYPOND_BIN = '$absbuild/$out/lilypond-bin ${__verbose}'
504 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py --verbose'
505
506
507 # post-option environment-update
508 env.Append (
509         bindir = bindir,
510         sharedir = sharedir,
511         lilypond_datadir = sharedir_package,
512         localedir = localedir,
513         local_lilypond_datadir = sharedir_package_version,
514         lilypondprefix = lilypondprefix,
515         sharedir_package = sharedir_package,
516         sharedir_doc_package = sharedir_doc_package,
517         sharedir_package_version = sharedir_package_version,
518
519         LILYPOND = BUILD_LILYPOND,
520         ABC2LY = BUILD_ABC2LY,
521         LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
522         LILYPOND_BOOK_FORMAT = 'texi-html',
523         MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
524
525         TEXI2DVI_PAPERSIZE = '@afourpaper',
526         TEXI2DVI_FLAGS = [ '-t $TEXI2DVI_PAPERSIZE'],
527         DVIPS_PAPERSIZE = 'a4',
528         DVIPS_FLAGS = ['-t $DVIPS_PAPERSIZE',
529                        '-u+lilypond.map',
530                        '-u+ec-mftrace.map'],
531         PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
532         )
533
534 if env['debugging']:
535         env.Append (CCFLAGS = ['-g', '-pipe'])
536 if env['optimising']:
537         env.Append (CCFLAGS = '-O2')
538         env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
539 if env['warnings']:
540         env.Append (CCFLAGS = ['-W', '-Wall'])
541         env.Append (CXXFLAGS = ['-Wconversion'])
542
543 # ugr,huh?
544 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
545
546 if env['verbose']:
547         env['__verbose'] = ' --verbose'
548         env['set__x'] = 'set -x;'
549
550
551 ## Explicit target and dependencies
552
553 if 'clean' in COMMAND_LINE_TARGETS:
554         # ugh: prevent reconfigure instead of clean
555         os.system ('touch %s' % config_cache)
556         
557         command = sys.argv[0] + ' -c .'
558         sys.stdout.write ('Running %s ... ' % command)
559         sys.stdout.write ('\n')
560         s = os.system (command)
561         if os.path.exists (config_cache):
562                 os.unlink (config_cache)
563         Exit (s)
564
565 if 'sconsclean' in COMMAND_LINE_TARGETS:
566         command = 'rm -rf scons.cache $(find . -name ".scon*")'
567         s = os.system (command)
568         if os.path.exists (config_cache):
569                 os.unlink (config_cache)
570         Exit (s)
571         
572 if 'realclean' in COMMAND_LINE_TARGETS:
573         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
574         sys.stdout.write ('Running %s ... ' % command)
575         sys.stdout.write ('\n')
576         s = os.system (command)
577         if os.path.exists (config_cache):
578                 os.unlink (config_cache)
579         Exit (s)
580
581 # Declare SConscript phonies 
582 env.Alias ('minimal', config_cache)
583 env.Alias ('mf-essential', config_cache)
584
585 env.Alias ('minimal', ['lily', 'mf-essential'])
586 env.Alias ('all', ['minimal', 'mf', '.'])
587 # Do we want the doc/web separation?
588 env.Alias ('doc',
589            ['Documentation',
590             'Documentation/user',
591             'Documentation/topdocs',
592             'Documentation/bibliography',
593             'input'])
594
595 # Without target arguments, do minimal build
596 if not COMMAND_LINE_TARGETS:
597         env.Default (['minimal'])
598
599 # GNU Make rerouting compat:
600 env.Alias ('web', 'doc')
601
602
603 env.Command (version_hh, '#/VERSION',
604              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
605
606 # post-config environment update
607 env.Append (
608         run_prefix = run_prefix,
609         LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
610
611         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),],
612         CPPPATH = [outdir, ],
613         LILYPOND_PATH = ['.', '$srcdir/input',
614                          '$srcdir/input/regression',
615                          '$srcdir/input/test',
616                          '$srcdir/input/tutorial',
617                          '$srcdir/Documentation/user',
618                          '$absbuild/mf/$out',
619 #                        os.path.join (absbuild, 'Documentation',
620 #                                      env['out']),
621 #                        os.path.join (absbuild, 'Documentation/user',
622 #                                      env['out']),
623                          ],
624         MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
625                          '$absbuild/Documentation/user/$out'],
626         )
627
628 def symlink_tree (target, source, env):
629         def mkdirs (dir):
630                 def mkdir (dir):
631                         if not dir:
632                                 os.chdir (os.sep)
633                                 return
634                         if not os.path.isdir (dir):
635                                 if os.path.exists (dir):
636                                         os.unlink (dir)
637                                 os.mkdir (dir)
638                         os.chdir (dir)
639                 map (mkdir, string.split (dir, os.sep))
640         def symlink (src, dst):
641                 os.chdir (absbuild)
642                 dir = os.path.dirname (dst)
643                 mkdirs (dir)
644                 if src[0] == '#':
645                         frm = os.path.join (srcdir, src[1:])
646                 else:
647                         depth = len (string.split (dir, '/'))
648                         if src.find ('@') > -1:
649                                 frm = os.path.join ('../' * depth,
650                                                     string.replace (src, '@',
651                                                                     env['out']))
652                         else:
653                                 frm = os.path.join ('../' * depth, src,
654                                                     env['out'])
655                 if src[-1] == '/':
656                         frm = os.path.join (frm, os.path.basename (dst))
657                 if env['verbose']:
658                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
659                 os.symlink (frm, os.path.basename (dst))
660         shutil.rmtree (run_prefix)
661         prefix = os.path.join (env['out'], 'usr')
662         map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
663              # ^# := source dir
664              # @  := out
665              # /$ := add dst file_name
666              (('python',     'lib/lilypond/python'),
667               ('lily/',      'bin/lilypond-bin'),
668               ('scripts/',   'bin/lilypond'),
669               ('scripts/',   'bin/lilypond-book'),
670               ('mf',         'share/lilypond/dvips'),
671               ('#ps',        'share/lilypond/tex/music-drawing-routines.ps'),
672               ('mf',         'share/lilypond/otf'),
673               ('mf',         'share/lilypond/tfm'),
674               ('#mf',        'share/lilypond/fonts/mf'),
675               ('mf',         'share/lilypond/fonts/otf'),
676               ('mf',         'share/lilypond/fonts/tfm'),
677               ('mf',         'share/lilypond/fonts/type1'),
678               ('#tex',       'share/lilypond/tex/source'),
679               ('tex',        'share/lilypond/tex/tex-out'),
680               ('mf',         'share/lilypond/tex/mf-out'),
681               ('#ly',        'share/lilypond/ly'),
682               ('#scm',       'share/lilypond/scm'),
683               ('#ps',        'share/lilypond/ps'),
684               ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
685               ('elisp',      'share/lilypond/elisp')))
686         os.chdir (srcdir)
687
688 if env['debugging']:
689         stamp = os.path.join (run_prefix, 'stamp')
690         env.Command (stamp, 'SConstruct', [symlink_tree, 'touch $TARGET'])
691         env.Depends ('lily', stamp)
692         
693 #### dist, tar
694 def plus (a, b):
695         a + b
696
697 def cvs_entry_is_dir (line):
698         return line[0] == 'D' and line[-2] == '/'
699
700 def cvs_entry_is_file (line):
701         return line[0] == '/' and line[-2] == '/'
702
703 def cvs_dirs (dir):
704         ENTRIES = os.path.join (dir, 'CVS/Entries')
705         if not os.path.exists (ENTRIES):
706                 return []
707         entries = open (ENTRIES).readlines ()
708         dir_entries = filter (cvs_entry_is_dir, entries)
709         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
710                     dir_entries)
711         return dirs + map (cvs_dirs, dirs)
712
713 def cvs_files (dir):
714         ENTRIES = os.path.join (dir, 'CVS/Entries')
715         entries = open (ENTRIES).readlines ()
716         file_entries = filter (cvs_entry_is_file, entries)
717         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
718         return map (lambda x: os.path.join (dir, x), files)
719
720 def flatten (tree, lst):
721         if type (tree) == type ([]):
722                 for i in tree:
723                         if type (i) == type ([]):
724                                 flatten (i, lst)
725                         else:
726                                 lst.append (i)
727         return lst
728
729 if env['fast']\
730    and 'all' not in COMMAND_LINE_TARGETS\
731    and 'doc' not in COMMAND_LINE_TARGETS\
732    and 'web' not in COMMAND_LINE_TARGETS\
733    and 'install' not in COMMAND_LINE_TARGETS\
734    and 'clean' not in COMMAND_LINE_TARGETS:
735         subdirs = ['lily', 'lily/include', 'flower', 'flower/include', 'mf']
736 else:
737         subdirs = flatten (cvs_dirs ('.'), [])
738
739 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
740 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
741 txt_files = map (lambda x: x + '.txt', readme_files)
742
743
744 #
745 # speeds up build by +- 5% 
746
747 if not env['fast']:
748         foo = map (lambda x: env.TXT (x + '.txt',
749                                       os.path.join ('Documentation/topdocs', x)),
750                    readme_files)
751         tar_base = package.name + '-' + version
752         tar_name = tar_base + '.tar.gz'
753         ball_prefix = os.path.join (outdir, tar_base)
754         tar_ball = os.path.join (outdir, tar_name)
755
756         dist_files = src_files + txt_files
757         ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
758         map (lambda x: env.Depends (tar_ball, x), ball_files)
759         map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
760                                     'ln $SOURCE $TARGET'), dist_files)
761         tar = env.Command (tar_ball, src_files,
762                            ['rm -f $$(find $TARGET.dir -name .sconsign)',
763                             'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
764         env.Alias ('tar', tar)
765
766         dist_ball = os.path.join (package.release_dir, tar_name)
767         env.Command (dist_ball, tar_ball,
768                      'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
769                      + 'ln $SOURCE $TARGET')
770         env.Depends ('dist', dist_ball)
771         patch_name = os.path.join (outdir, tar_base + '.diff.gz')
772         patch = env.PATCH (patch_name, tar_ball)
773         env.Depends (patch_name, dist_ball)
774         env.Alias ('release', patch)
775
776 #### web
777 if not env['fast']:
778         web_base = os.path.join (outdir, 'web')
779         web_ball = web_base + '.tar.gz'
780         env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
781         web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
782         web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"')
783         env['web_path'] = web_path
784         web_list = os.path.join (outdir, 'weblist')
785         # compatible make heritits
786         # fixme: generate in $outdir is cwd/builddir
787         env.Command (web_list,
788                      ## this is correct, but takes > 5min if you have a peder :-)
789                      ##'doc',
790                      '#/VERSION',
791                      ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
792                       'cd $absbuild && $footify $$(find . -name "*.html" -print)',
793                       'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
794                       'cd $absbuild && find Documentation input $web_path \
795                       > $TARGET',
796                       '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
797                       '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
798                       'cd $absbuild && ls *.html >> $TARGET',])
799         env.Command (web_ball, web_list,
800                      ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
801         #env.Alias ('web', web_ball)
802         www_base = os.path.join (outdir, 'www')
803         www_ball = www_base + '.tar.gz'
804         env.Command (www_ball, web_ball,
805                      ['rm -rf $out/tmp',
806                       'mkdir -p $absbuild/$out/tmp',
807                       'tar -C $absbuild/$out/tmp -xzf $SOURCE',
808                       'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
809                       + ' do mv $$i $$(dirname $$i)/out-www; done',
810                       'tar -C $absbuild/$out/tmp -czf $TARGET .'])
811         env.Alias ('web', www_ball)
812
813 #### tags
814 env.Append (
815         ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
816         --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
817 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
818 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
819                              src_files),
820              'etags $ETAGSFLAGS $SOURCES')
821
822 # Note: SConscripts are only needed in directories where something needs
823 # to be done, building or installing
824 for d in subdirs:
825         if os.path.exists (os.path.join (d, 'SConscript')):
826                 b = os.path.join (env['build'], d, env['out'])
827                 # Support clean sourcetree build (--srcdir build)
828                 # and ./out build.
829                 if os.path.abspath (b) != os.path.abspath (d):
830                         env.BuildDir (b, d, duplicate = 0)
831                 SConscript (os.path.join (b, 'SConscript'))
832