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