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