]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* mf/SConscript: Build map files.
[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 = {
471         'TEXMF' : '{$LILYPONDPREFIX,' \
472         + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
473         'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond'),
474         })
475
476 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
477 BUILD_LILYPOND = '${set__x}$PYTHON $srcdir/scripts/lilypond.py${__verbose}'
478 BUILD_LILYPOND_BIN = '$absbuild/$out/lilypond-bin ${__verbose}'
479 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py --verbose'
480
481
482 # post-option environment-update
483 env.Append (
484         bindir = bindir,
485         sharedir = sharedir,
486         lilypond_datadir = sharedir_package,
487         localedir = localedir,
488         local_lilypond_datadir = sharedir_package_version,
489         lilypondprefix = lilypondprefix,
490         sharedir_package = sharedir_package,
491         sharedir_doc_package = sharedir_doc_package,
492         sharedir_package_version = sharedir_package_version,
493
494         LILYPOND = BUILD_LILYPOND,
495         ABC2LY = BUILD_ABC2LY,
496         LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
497         LILYPOND_BOOK_FORMAT = 'texi-html',
498         MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
499
500         TEXI2DVI_PAPERSIZE = '@afourpaper',
501         TEXI2DVI_FLAGS = [ '-t $TEXI2DVI_PAPERSIZE'],
502         DVIPS_PAPERSIZE = 'a4',
503         DVIPS_FLAGS = ['-t $DVIPS_PAPERSIZE',
504                        '-u+lilypond.map',
505                        '-u+ec-mftrace.map'],
506         PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
507         )
508
509 if env['debugging']:
510         env.Append (CCFLAGS = ['-g', '-pipe'])
511 if env['optimising']:
512         env.Append (CCFLAGS = '-O2')
513         env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
514 if env['warnings']:
515         env.Append (CCFLAGS = ['-W', '-Wall'])
516         env.Append (CXXFLAGS = ['-Wconversion'])
517
518 # ugr,huh?
519 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
520
521 if env['verbose']:
522         env['__verbose'] = ' --verbose'
523         env['set__x'] = 'set -x;'
524
525
526 ## Explicit target and dependencies
527
528 if 'clean' in COMMAND_LINE_TARGETS:
529         # ugh: prevent reconfigure instead of clean
530         os.system ('touch %s' % config_cache)
531         
532         command = sys.argv[0] + ' -c .'
533         sys.stdout.write ('Running %s ... ' % command)
534         sys.stdout.write ('\n')
535         s = os.system (command)
536         if os.path.exists (config_cache):
537                 os.unlink (config_cache)
538         Exit (s)
539
540 if 'realclean' in COMMAND_LINE_TARGETS:
541         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
542         sys.stdout.write ('Running %s ... ' % command)
543         sys.stdout.write ('\n')
544         s = os.system (command)
545         if os.path.exists (config_cache):
546                 os.unlink (config_cache)
547         Exit (s)
548
549 # Declare SConscript phonies 
550 env.Alias ('minimal', config_cache)
551 env.Alias ('mf-essential', config_cache)
552
553 env.Alias ('minimal', ['lily', 'mf-essential'])
554 env.Alias ('all', ['minimal', 'mf', '.'])
555 # Do we want the doc/web separation?
556 env.Alias ('doc',
557            ['Documentation',
558             'Documentation/user',
559             'Documentation/topdocs',
560             'Documentation/bibliography',
561             'input'])
562
563 # Without target arguments, do minimal build
564 if not COMMAND_LINE_TARGETS:
565         env.Default (['minimal'])
566
567 # GNU Make rerouting compat:
568 env.Alias ('web', 'doc')
569
570
571 env.Command (version_hh, '#/VERSION',
572              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
573
574 # post-config environment update
575 env.Append (
576         run_prefix = run_prefix,
577         LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
578
579         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),],
580         ##CPPPATH = [outdir, '#',], # do not read auto*'s header
581         CPPPATH = [outdir, ],
582         LILYPOND_PATH = ['.', '$srcdir/input',
583                          '$srcdir/input/regression',
584                          '$srcdir/input/test',
585                          '$srcdir/input/tutorial',
586                          '$srcdir/Documentation/user',
587                          '$absbuild/mf/$out',
588 #                        os.path.join (absbuild, 'Documentation',
589 #                                      env['out']),
590 #                        os.path.join (absbuild, 'Documentation/user',
591 #                                      env['out']),
592                          ],
593         MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
594                          '$absbuild/Documentation/user/$out'],
595         )
596
597 def symlink_tree (target, source, env):
598         def mkdirs (dir):
599                 def mkdir (dir):
600                         if not dir:
601                                 os.chdir (os.sep)
602                                 return
603                         if not os.path.isdir (dir):
604                                 if os.path.exists (dir):
605                                         os.unlink (dir)
606                                 os.mkdir (dir)
607                         os.chdir (dir)
608                 map (mkdir, string.split (dir, os.sep))
609         def symlink (src, dst):
610                 os.chdir (absbuild)
611                 dir = os.path.dirname (dst)
612                 mkdirs (dir)
613                 if src[0] == '#':
614                         frm = os.path.join (srcdir, src[1:])
615                 else:
616                         depth = len (string.split (dir, '/'))
617                         if src.find ('@') > -1:
618                                 frm = os.path.join ('../' * depth,
619                                                     string.replace (src, '@',
620                                                                     env['out']))
621                         else:
622                                 frm = os.path.join ('../' * depth, src,
623                                                     env['out'])
624                 if src[-1] == '/':
625                         frm = os.path.join (frm, os.path.basename (dst))
626                 if env['verbose']:
627                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
628                 os.symlink (frm, os.path.basename (dst))
629         shutil.rmtree (run_prefix)
630         prefix = os.path.join (env['out'], 'usr')
631         map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
632              # ^# := source dir
633              # @  := out
634              # /$ := add dst file_name
635              (('python',     'lib/lilypond/python'),
636               ('lily/',      'bin/lilypond-bin'),
637               ('scripts/',   'bin/lilypond'),
638               ('scripts/',   'bin/lilypond-book'),
639               ('mf',         'share/lilypond/dvips'),
640               ('#ps',        'share/lilypond/tex/music-drawing-routines.ps'),
641               ('mf',         'share/lilypond/afm'),
642               ('mf',         'share/lilypond/tfm'),
643               ('#mf',        'share/lilypond/fonts/mf'),
644               ('mf',         'share/lilypond/fonts/afm'),
645               ('mf',         'share/lilypond/fonts/tfm'),
646               ('mf',         'share/lilypond/fonts/type1'),
647               ('#tex',       'share/lilypond/tex/source'),
648               ('mf',         'share/lilypond/tex/generate'),
649               ('#ly',        'share/lilypond/ly'),
650               ('#scm',       'share/lilypond/scm'),
651               ('#ps',        'share/lilypond/ps'),
652               ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
653               ('elisp',      'share/lilypond/elisp')))
654         os.chdir (srcdir)
655
656 if env['debugging']:
657         stamp = os.path.join (run_prefix, 'stamp')
658         env.Command (stamp, 'SConstruct', [symlink_tree, 'touch $TARGET'])
659         env.Depends ('lily', stamp)
660         
661 #### dist, tar
662 def plus (a, b):
663         a + b
664
665 def cvs_entry_is_dir (line):
666         return line[0] == 'D' and line[-2] == '/'
667
668 def cvs_entry_is_file (line):
669         return line[0] == '/' and line[-2] == '/'
670
671 def cvs_dirs (dir):
672         ENTRIES = os.path.join (dir, 'CVS/Entries')
673         if not os.path.exists (ENTRIES):
674                 return []
675         entries = open (ENTRIES).readlines ()
676         dir_entries = filter (cvs_entry_is_dir, entries)
677         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
678                     dir_entries)
679         return dirs + map (cvs_dirs, dirs)
680
681 def cvs_files (dir):
682         ENTRIES = os.path.join (dir, 'CVS/Entries')
683         entries = open (ENTRIES).readlines ()
684         file_entries = filter (cvs_entry_is_file, entries)
685         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
686         return map (lambda x: os.path.join (dir, x), files)
687
688 def flatten (tree, lst):
689         if type (tree) == type ([]):
690                 for i in tree:
691                         if type (i) == type ([]):
692                                 flatten (i, lst)
693                         else:
694                                 lst.append (i)
695         return lst
696
697 subdirs = flatten (cvs_dirs ('.'), [])
698 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
699 foo = map (lambda x: env.TXT (x + '.txt',
700                               os.path.join ('Documentation/topdocs', x)),
701            readme_files)
702 txt_files = map (lambda x: x + '.txt', readme_files)
703 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
704 tar_base = package.name + '-' + version
705 tar_name = tar_base + '.tar.gz'
706 ball_prefix = os.path.join (outdir, tar_base)
707 tar_ball = os.path.join (outdir, tar_name)
708
709 dist_files = src_files + txt_files
710 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
711 map (lambda x: env.Depends (tar_ball, x), ball_files)
712 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
713                             'ln $SOURCE $TARGET'), dist_files)
714 tar = env.Command (tar_ball, src_files,
715                    ['rm -f $$(find $TARGET.dir -name .sconsign)',
716                     'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
717 env.Alias ('tar', tar)
718
719 dist_ball = os.path.join (package.release_dir, tar_name)
720 env.Command (dist_ball, tar_ball,
721              'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
722              + 'ln $SOURCE $TARGET')
723 env.Depends ('dist', dist_ball)
724 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
725 patch = env.PATCH (patch_name, tar_ball)
726 env.Depends (patch_name, dist_ball)
727 env.Alias ('release', patch)
728
729 #### web
730 web_base = os.path.join (outdir, 'web')
731 web_ball = web_base + '.tar.gz'
732 env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
733 web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
734 web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"')
735 env['web_path'] = web_path
736 web_list = os.path.join (outdir, 'weblist')
737 # compatible make heritits
738 # fixme: generate in $outdir is cwd/builddir
739 env.Command (web_list,
740              ## this is correct, but takes > 5min if you have a peder :-)
741              ##'doc',
742              '#/VERSION',
743              ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
744               'cd $absbuild && $footify $$(find . -name "*.html" -print)',
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               'cd $absbuild && ls *.html >> $TARGET',])
751 env.Command (web_ball, web_list,
752              ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
753 #env.Alias ('web', web_ball)
754 www_base = os.path.join (outdir, 'www')
755 www_ball = www_base + '.tar.gz'
756 env.Command (www_ball, web_ball,
757              ['rm -rf $out/tmp',
758               'mkdir -p $absbuild/$out/tmp',
759               'tar -C $absbuild/$out/tmp -xzf $SOURCE',
760               'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"; do mv $i out-www; done',
761               'tar -C $absbuild/$out/tmp -czf $TARGET .'])
762 env.Alias ('web', www_ball)
763
764 #### tags
765 env.Append (
766         ETAGSFLAGS = ["""--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\1/'""",
767                       """--regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\1/'"""])
768 # filter-out some files?
769 env.Command ('TAGS', src_files, 'etags $ETAGSFLAGS $SOURCES')
770
771
772 # Note: SConscripts are only needed in directories where something needs
773 # to be done, building or installing
774 for d in subdirs:
775         if os.path.exists (os.path.join (d, 'SConscript')):
776                 b = os.path.join (env['build'], d, env['out'])
777                 # Support clean sourcetree build (--srcdir build)
778                 # and ./out build.
779                 if os.path.abspath (b) != os.path.abspath (d):
780                         env.BuildDir (b, d, duplicate = 0)
781                 SConscript (os.path.join (b, 'SConscript'))
782