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