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