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