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