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