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