]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
update SConstruct version reqs. No --verbose if env['verbose'] == '0'
[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', '4.0', 'GNU C compiler', 'gcc')
423     test_program (required, 'g++', '4.0.5', 'GNU C++ compiler', 'g++')
424     test_program (required, 'guile-config', '1.8', '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.19',
428               'mftrace (http://xs4all.nl/~hanwen/mftrace)', 'mftrace')
429     test_program (required, 'python', '2.1', 'Python (www.python.org)',
430               'python')
431     # Silly, and breaks with /bin/sh == dash
432     #test_program (required, 'sh', '0.0', 'Bourne shell', 'sh')
433
434     optional = []
435     # Do not use bison 1.50 and 1.75.
436     #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
437     test_program (optional, 'bison', '1.25', 'Bison -- parser generator',
438             'bison')
439     test_program (optional, 'fontforge', '0.0.20050624', 'FontForge',
440               'fontforge')
441     test_program (optional, 'flex', '0.0', 'Flex -- lexer generator',
442               'flex')
443     test_program (optional, 'guile', '1.8', 'GUILE scheme', 'guile')
444     test_program (optional, 'gs', '8.15',
445               'Ghostscript PostScript interpreter',
446               'gs or gs-afpl or gs-esp or gs-gpl')
447     test_program (optional, 'makeinfo', '4.8', 'Makeinfo tool', 'texinfo')
448     test_program (optional, 'perl', '4.0',
449               'Perl practical efficient readonly language', 'perl')
450
451     def CheckYYCurrentBuffer (context):
452         context.Message ('Checking for yy_current_buffer... ')
453         ret = conf.TryCompile ("""using namespace std;
454         #include <FlexLexer.h>
455         class yy_flex_lexer: public yyFlexLexer
456         {
457         public:
458         yy_flex_lexer ()
459         {
460         yy_current_buffer = 0;
461         }
462         };""", '.cc')
463         context.Result (ret)
464         return ret
465
466     conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
467                         : CheckYYCurrentBuffer })
468
469     defines = {
470        'DIRSEP' : "'%s'" % os.sep,
471        'PATHSEP' : "'%s'" % os.pathsep,
472        'PACKAGE': '"%s"' % package.name,
473        'DATADIR' : '"%s"' % sharedir,
474        'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
475        'LOCALEDIR' : '"%s"' %localedir,
476     }
477     conf.env.Append (DEFINES = defines)
478
479     command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
480     PYTHON_INCLUDE = os.popen (command).read ()#[:-1]
481     if env['fast']:
482         env.Append (CCFLAGS = ['-I%s' % PYTHON_INCLUDE])
483     else:
484         env.Append (CPPPATH = [PYTHON_INCLUDE])
485
486     headers = ('assert.h', 'grp.h', 'libio.h', 'pwd.h',
487            'sys/stat.h', 'utf8/wchar.h', 'wchar.h', 'Python.h')
488     for i in headers:
489         if conf.CheckCHeader (i):
490             key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
491             conf.env['DEFINES'][key] = 1
492
493     ccheaders = ('sstream',)
494     for i in ccheaders:
495         if conf.CheckCXXHeader (i):
496             key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
497             conf.env['DEFINES'][key] = 1
498
499     functions = ('chroot', 'fopencookie', 'funopen',
500              'gettext', 'isinf',
501              'mbrtowc', 'memmem', 'snprintf', 'vsnprintf', 'wcrtomb')
502     for i in functions:
503         if 0 or conf.CheckFunc (i):
504             key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
505             conf.env['DEFINES'][key] = 1
506
507     if conf.CheckYYCurrentBuffer ():
508         conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
509
510     if conf.CheckLib ('dl'):
511         pass
512
513     if env['fast']:
514         cpppath = []
515         if env.has_key ('CPPPATH'):
516             cpppath = env['CPPPATH']
517
518     ## FIXME: linkage, check for libguile.h and scm_boot_guile
519     #this could happen after flower...
520     env.ParseConfig ('guile-config compile')
521
522     test_program (required, 'pkg-config', '0.9.0',
523               'pkg-config library compile manager', 'pkg-config')
524     if test_lib (required, 'freetype2', '0.0',
525              'Development files for FreeType 2 font engine',
526              'freetype6'):
527         conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
528         
529     if test_lib (required, 'pangoft2', '1.6.0',
530              'Development files for pango, with FreeType2',
531              'pango1.0'):
532         conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
533
534     if test_lib (optional, 'fontconfig', '2.2.0',
535              'Development files for fontconfig', 'fontconfig1'):
536         conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
537     
538     #this could happen only for compiling pango-*
539     if env['gui']:
540         test_lib (required, 'gtk+-2.0', '2.4.0',
541               'Development files for GTK+', 'gtk2.0')
542             
543     if env['fast']:
544         # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
545         # <system-dir>] speeds up SCons
546         env['CCFLAGS'] += map (lambda x: '-I' + x,
547                        env['CPPPATH'][len (cpppath):])
548         env['CPPPATH'] = cpppath
549
550     if required:
551         print
552         print '********************************'
553         print 'Please install required packages'
554         for i in required:
555             print '%s:      %s-%s or newer (found: %s %s)' % i
556         Exit (1)
557
558     if optional:
559         print
560         print '*************************************'
561         print 'Consider installing optional packages'
562         for i in optional:
563             print '%s:      %s-%s or newer (found: %s %s)' % i
564
565     return conf.Finish ()
566
567 def config_header (target, source, env):
568     config = open (str (target[0]), 'w')
569     for i in sorted (env['DEFINES'].keys ()):
570         config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
571     config.close ()
572 env.Command (config_hh, config_cache, config_header)
573
574 # hmm?
575 def xuniquify (lst):
576     n = []
577     for i in lst:
578         if not i in n:
579             n.append (i)
580     lst = n
581     return lst
582
583 def uniquify (lst):
584     d = {}
585     n = len (lst)
586     i = 0
587     while i < n:
588         if not d.has_key (lst[i]):
589             d[lst[i]] = 1
590             i += 1
591         else:
592             del lst[i]
593             n -= 1
594     return lst
595
596 def uniquify_config_vars (env):
597     for i in config_vars:
598         if env.has_key (i) and type (env[i]) == type ([]):
599             env[i] = uniquify (env[i])
600
601 def save_config_cache (env):
602     ## FIXME: Is this smart, using option cache for saving
603     ## config.cache?  I cannot seem to find the official method.
604     uniquify_config_vars (env)
605     opts.Save (config_cache, env)
606
607     if 'config' in COMMAND_LINE_TARGETS:
608         sys.stdout.write ('\n')
609         sys.stdout.write ('LilyPond configured')
610         sys.stdout.write ('\n')
611         sys.stdout.write ('Now run')
612         sys.stdout.write ('\n')
613         sys.stdout.write ('    scons [TARGET|DIR]...')
614         sys.stdout.write ('\n')
615         sys.stdout.write ('\n')
616         sys.stdout.write ('Examples:')
617         sys.stdout.write ('\n')
618         sys.stdout.write ('    scons lily    # build lilypond')
619         sys.stdout.write ('\n')
620         sys.stdout.write ('    scons all     # build everything')
621         sys.stdout.write ('\n')
622         sys.stdout.write ('    scons doc     # build documentation')
623         sys.stdout.write ('\n')
624         ## TODO
625         ## sys.stdout.write ('    scons prefix=/usr DESTDIR=/tmp/pkg all install')
626         ## sys.stdout.write ('\n')
627         Exit (0)
628     elif not env['checksums']:
629         # When using timestams, config.hh is NEW.  The next
630         # build triggers recompilation of everything.  Exiting
631         # here makes SCons use the actual timestamp for config.hh
632         # and prevents recompiling everything the next run.
633         command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
634         sys.stdout.write ('Running %s ... ' % command)
635         sys.stdout.write ('\n')
636         s = os.system (command)
637         Exit (s)
638
639 # WTF?
640 # scons: *** Calling Configure from Builders is not supported.
641 # env.Command (config_cache, None, configure)
642 if not os.path.exists (config_cache) \
643    or (os.stat ('SConstruct')[stat.ST_MTIME]
644        > os.stat (config_cache)[stat.ST_MTIME]):
645     env = configure (None, None, env)
646     save_config_cache (env)
647 elif env['checksums']:
648     # just save everything
649     save_config_cache (env)
650
651 #urg how does #/ subst work?
652 Export ('env')
653 SConscript ('buildscripts/builder.py')
654
655 env.PrependENVPath ('PATH',
656             os.path.join (env['absbuild'], env['out'], 'usr/bin'))
657
658 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond/', version)
659
660 if not os.path.exists (LILYPONDPREFIX):
661     os.makedirs (LILYPONDPREFIX)
662
663 env.Command (LILYPONDPREFIX, ['#/SConstruct', '#/VERSION'], symlink_tree)
664 env.Depends ('lily', LILYPONDPREFIX)
665
666 env.Append (ENV = {
667     'LILYPONDPREFIX' : LILYPONDPREFIX,
668     'TEXMF' : '{$LILYPONDPREFIX,'
669     + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
670     })
671
672 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
673 BUILD_LILYPOND = '$absbuild/lily/$out/lilypond ${__verbose}'
674 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py ${__verbose}'
675
676
677 # post-option environment-update
678 env.Append (
679     bindir = bindir,
680     sharedir = sharedir,
681     lilypond_datadir = sharedir_package,
682     localedir = localedir,
683     local_lilypond_datadir = sharedir_package_version,
684     lilypondprefix = lilypondprefix,
685     sharedir_package = sharedir_package,
686     sharedir_doc_package = sharedir_doc_package,
687     sharedir_package_version = sharedir_package_version,
688     libdir_package = libdir_package,
689     libdir_package_version = libdir_package_version,
690
691     # global build verbosity switch
692     __verbose = ' --verbose',
693     
694     LILYPOND = BUILD_LILYPOND,
695     ABC2LY = BUILD_ABC2LY,
696     LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
697     LILYPOND_BOOK_FORMAT = 'texi-html',
698     MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
699     )
700
701 env.Append (CCFLAGS = ['-pipe', '-Wno-pmf-conversions'])
702 if env['debugging']:
703     env.Append (CCFLAGS = ['-g'])
704 if env['optimising']:
705     env.Append (CCFLAGS = '-O2')
706 if env['warnings']:
707     env.Append (CCFLAGS = ['-W', '-Wall'])
708     env.Append (CXXFLAGS = ['-Wconversion'])
709
710 # ugr,huh?
711 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
712 # FIXME: ParseConfig ignores -L flag?
713 env.Append (LINKFLAGS = ['-L/usr/X11R6/lib'])
714
715 ## UGH? 
716 if env['verbose'] and env['verbose'] <> '0':
717     env['__verbose'] = ' --verbose'
718     env['set__x'] = 'set -x;'
719
720
721 ## Explicit target and dependencies
722
723 if 'clean' in COMMAND_LINE_TARGETS:
724     # ugh: prevent reconfigure instead of clean
725     os.system ('touch %s' % config_cache)
726     
727     command = sys.argv[0] + ' -c .'
728     sys.stdout.write ('Running %s ... ' % command)
729     sys.stdout.write ('\n')
730     s = os.system (command)
731     if os.path.exists (config_cache):
732         os.unlink (config_cache)
733     Exit (s)
734
735 if 'sconsclean' in COMMAND_LINE_TARGETS:
736     command = 'rm -rf scons.cache $(find . -name ".scon*")'
737     s = os.system (command)
738     if os.path.exists (config_cache):
739         os.unlink (config_cache)
740     Exit (s)
741     
742 if 'realclean' in COMMAND_LINE_TARGETS:
743     command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
744     sys.stdout.write ('Running %s ... ' % command)
745     sys.stdout.write ('\n')
746     s = os.system (command)
747     if os.path.exists (config_cache):
748         os.unlink (config_cache)
749     Exit (s)
750
751 def symlink_tree ():
752     print "BOE"
753     raise urg
754     
755 # Declare SConscript phonies 
756 env.Alias ('minimal', config_cache)
757
758 if 0:
759     env.Alias ('mf-essential', config_cache)
760     env.Alias ('minimal', ['python', 'lily', 'mf-essential'])
761     env.Alias ('all', ['minimal', 'mf', '.'])
762
763 else:
764     env.Alias ('minimal', ['python', 'lily', 'mf'])
765     env.Alias ('all', ['minimal', '.'])
766
767
768 # Do we want the doc/web separation?
769 env.Alias ('doc',
770        ['minimal',
771         'Documentation',
772         'Documentation/user',
773         'Documentation/topdocs',
774         'Documentation/bibliography',
775         'input'])
776
777 # Without target arguments, do minimal build
778 if not COMMAND_LINE_TARGETS:
779     env.Default (['minimal'])
780
781 # GNU Make rerouting compat:
782 env.Alias ('web', 'doc')
783
784
785 env.Command (version_hh, '#/VERSION',
786          '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
787
788 # post-config environment update
789 env.Append (
790     run_prefix = run_prefix,
791     LILYPONDPREFIX = LILYPONDPREFIX,
792
793     # FIXME: move to lily/SConscript?
794     LIBPATH = [os.path.join (absbuild, 'flower', env['out'])],
795     CPPPATH = [outdir, ],
796     LILYPOND_PATH = ['.',
797              '$srcdir/input',
798              '$srcdir/input/regression',
799              '$srcdir/input/test',
800              '$srcdir/input/tutorial',
801              '$srcdir/Documentation/user',
802              '$absbuild/mf/$out',
803 #            os.path.join (absbuild, 'Documentation',
804 #                      env['out']),
805 #            os.path.join (absbuild, 'Documentation/user',
806 #                      env['out']),
807              ],
808     MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
809              '$absbuild/Documentation/user/$out'],
810     )
811
812 #### dist, tar
813 def plus (a, b):
814     a + b
815
816 def cvs_entry_is_dir (line):
817     return line[0] == 'D' and line[-2] == '/'
818
819 def cvs_entry_is_file (line):
820     return line[0] == '/' and line[-2] == '/'
821
822 def cvs_dirs (dir):
823     entries = os.path.join (dir, 'CVS/Entries')
824     if not os.path.exists (entries):
825         return []
826     entries = open (entries).readlines ()
827     dir_entries = filter (cvs_entry_is_dir, entries)
828     dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
829             dir_entries)
830     return dirs + map (cvs_dirs, dirs)
831
832 def cvs_files (dir):
833     entries = os.path.join (dir, 'CVS/Entries')
834     if not os.path.exists (entries):
835         return []
836     entries = open (entries).readlines ()
837     file_entries = filter (cvs_entry_is_file, entries)
838     files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
839     return map (lambda x: os.path.join (dir, x), files)
840
841 def flatten (tree, lst):
842     if type (tree) == type ([]):
843         for i in tree:
844             if type (i) == type ([]):
845                 flatten (i, lst)
846             else:
847                 lst.append (i)
848     return lst
849
850 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
851     subdirs = flatten (cvs_dirs ('.'), [])
852 else:
853     # ugh
854     command = 'cd %(srcdir)s \
855     && find . -name SConscript | sed s@/SConscript@@' % vars ()
856     subdirs = string.split (os.popen (command).read ())
857
858 if env['fast']\
859    and 'all' not in COMMAND_LINE_TARGETS\
860    and 'doc' not in COMMAND_LINE_TARGETS\
861    and 'web' not in COMMAND_LINE_TARGETS\
862    and 'install' not in COMMAND_LINE_TARGETS\
863    and 'clean' not in COMMAND_LINE_TARGETS:
864     subdirs = [ 'python',
865             'lily',
866            'flower',
867            'mf',
868            ]
869
870 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
871     src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
872 else:
873     src_files = ['foobar']
874
875 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
876 txt_files = map (lambda x: x + '.txt', readme_files)
877
878
879 #
880 # speeds up build by +- 5% 
881
882 if not env['fast']:
883     foo = map (lambda x: env.TXT (x + '.txt',
884                       os.path.join ('Documentation/topdocs', x)),
885            readme_files)
886     tar_base = package.name + '-' + version
887     tar_name = tar_base + '.tar.gz'
888     ball_prefix = os.path.join (outdir, tar_base)
889     tar_ball = os.path.join (outdir, tar_name)
890
891     dist_files = src_files + txt_files
892     ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
893     map (lambda x: env.Depends (tar_ball, x), ball_files)
894     map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
895                     'ln $SOURCE $TARGET'), dist_files)
896     tar = env.Command (tar_ball, src_files,
897                ['rm -f $$(find $TARGET.dir -name .sconsign)',
898                 'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
899     env.Alias ('tar', tar)
900
901     dist_ball = os.path.join (package.release_dir, tar_name)
902     env.Command (dist_ball, tar_ball,
903              'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
904              + 'ln $SOURCE $TARGET')
905     env.Depends ('dist', dist_ball)
906     patch_name = os.path.join (outdir, tar_base + '.diff.gz')
907     patch = env.PATCH (patch_name, tar_ball)
908     env.Depends (patch_name, dist_ball)
909     env.Alias ('release', patch)
910
911 #### web
912 if not env['fast']:
913     web_base = os.path.join (outdir, 'web')
914     web_ball = web_base + '.tar.gz'
915     env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
916     web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
917     web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
918     env['web_path'] = web_path
919     web_list = os.path.join (outdir, 'weblist')
920     # compatible make heritits
921     # fixme: generate in $outdir is cwd/builddir
922     env.Command (web_list,
923              ## Adding 'doc' dependency is correct, but takes
924              ## > 5min extra if you have a peder :-)
925              #'doc',
926              
927              '#/VERSION',
928              ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
929               'cd $absbuild && $footify $$(find . -name "*.html" -print)',
930               'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
931               'cd $absbuild && find Documentation input $web_path \
932               > $TARGET',
933               '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
934               '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
935               'cd $absbuild && ls *.html >> $TARGET',])
936     env.Command (web_ball, web_list,
937              ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
938     #env.Alias ('web', web_ball)
939     www_base = os.path.join (outdir, 'www')
940     www_ball = www_base + '.tar.gz'
941     env.Command (www_ball, web_ball,
942              ['rm -rf $out/tmp',
943               'mkdir -p $absbuild/$out/tmp',
944               'tar -C $absbuild/$out/tmp -xzf $SOURCE',
945               'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
946               + ' do mv $$i $$(dirname $$i)/out-www; done',
947               'tar -C $absbuild/$out/tmp -czf $TARGET .'])
948     env.Alias ('web', www_ball)
949
950 #### tags
951 env.Append (
952     ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
953     --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
954 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
955 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
956                  src_files),
957          'etags $ETAGSFLAGS $SOURCES')
958
959 # Note: SConscripts are only needed in directories where something needs
960 # to be done, building or installing
961 for d in subdirs:
962     if os.path.exists (os.path.join (d, 'SConscript')):
963         b = os.path.join (env['build'], d, env['out'])
964         # Support clean sourcetree build (--srcdir build)
965         # and ./out build.
966         if os.path.abspath (b) != os.path.abspath (d):
967             env.BuildDir (b, d, duplicate = 0)
968         SConscript (os.path.join (b, 'SConscript'))
969