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