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