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