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