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