]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
Only use color in png if necessary.
[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 if env['verbose'] and env['verbose'] != '0':
677     env['__verbose'] = ' --verbose'
678     env['set__x'] = 'set -x;'
679
680 # post-option environment-update
681 env.Append (
682     bindir = bindir,
683     sharedir = sharedir,
684     lilypond_datadir = sharedir_package,
685     localedir = localedir,
686     local_lilypond_datadir = sharedir_package_version,
687     lilypondprefix = lilypondprefix,
688     sharedir_package = sharedir_package,
689     sharedir_doc_package = sharedir_doc_package,
690     sharedir_package_version = sharedir_package_version,
691     libdir_package = libdir_package,
692     libdir_package_version = libdir_package_version,
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 ## Explicit target and dependencies
716
717 if 'clean' in COMMAND_LINE_TARGETS:
718     # ugh: prevent reconfigure instead of clean
719     os.system ('touch %s' % config_cache)
720     
721     command = sys.argv[0] + ' -c .'
722     sys.stdout.write ('Running %s ... ' % command)
723     sys.stdout.write ('\n')
724     s = os.system (command)
725     if os.path.exists (config_cache):
726         os.unlink (config_cache)
727     Exit (s)
728
729 if 'sconsclean' in COMMAND_LINE_TARGETS:
730     command = 'rm -rf scons.cache $(find . -name ".scon*")'
731     s = os.system (command)
732     if os.path.exists (config_cache):
733         os.unlink (config_cache)
734     Exit (s)
735     
736 if 'realclean' in COMMAND_LINE_TARGETS:
737     command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
738     sys.stdout.write ('Running %s ... ' % command)
739     sys.stdout.write ('\n')
740     s = os.system (command)
741     if os.path.exists (config_cache):
742         os.unlink (config_cache)
743     Exit (s)
744
745 # Declare SConscript phonies 
746 env.Alias ('minimal', config_cache)
747
748 if 0:
749     env.Alias ('mf-essential', config_cache)
750     env.Alias ('minimal', ['python', 'lily', 'mf-essential'])
751     env.Alias ('all', ['minimal', 'mf', '.'])
752
753 else:
754     env.Alias ('minimal', ['python', 'lily', 'mf'])
755     env.Alias ('all', ['minimal', '.'])
756
757
758 # Do we want the doc/web separation?
759 env.Alias ('doc',
760        ['minimal',
761         'Documentation',
762         'Documentation/user',
763         'Documentation/topdocs',
764         'Documentation/bibliography',
765         'input'])
766
767 # Without target arguments, do minimal build
768 if not COMMAND_LINE_TARGETS:
769     env.Default (['minimal'])
770
771 # GNU Make rerouting compat:
772 env.Alias ('web', 'doc')
773
774
775 env.Command (version_hh, '#/VERSION',
776          '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
777
778 # post-config environment update
779 env.Append (
780     run_prefix = run_prefix,
781     LILYPONDPREFIX = LILYPONDPREFIX,
782
783     # FIXME: move to lily/SConscript?
784     LIBPATH = [os.path.join (absbuild, 'flower', env['out'])],
785     CPPPATH = [outdir, ],
786     LILYPOND_PATH = ['.',
787              '$srcdir/input',
788              '$srcdir/input/regression',
789              '$srcdir/input/test',
790              '$srcdir/input/tutorial',
791              '$srcdir/Documentation/user',
792              '$absbuild/mf/$out',
793 #            os.path.join (absbuild, 'Documentation',
794 #                      env['out']),
795 #            os.path.join (absbuild, 'Documentation/user',
796 #                      env['out']),
797              ],
798     MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
799              '$absbuild/Documentation/user/$out'],
800     )
801
802 #### dist, tar
803 def plus (a, b):
804     a + b
805
806 def cvs_entry_is_dir (line):
807     return line[0] == 'D' and line[-2] == '/'
808
809 def cvs_entry_is_file (line):
810     return line[0] == '/' and line[-2] == '/'
811
812 def cvs_dirs (dir):
813     entries = os.path.join (dir, 'CVS/Entries')
814     if not os.path.exists (entries):
815         return []
816     entries = open (entries).readlines ()
817     dir_entries = filter (cvs_entry_is_dir, entries)
818     dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
819             dir_entries)
820     return dirs + map (cvs_dirs, dirs)
821
822 def cvs_files (dir):
823     entries = os.path.join (dir, 'CVS/Entries')
824     if not os.path.exists (entries):
825         return []
826     entries = open (entries).readlines ()
827     file_entries = filter (cvs_entry_is_file, entries)
828     files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
829     return map (lambda x: os.path.join (dir, x), files)
830
831 def flatten (tree, lst):
832     if type (tree) == type ([]):
833         for i in tree:
834             if type (i) == type ([]):
835                 flatten (i, lst)
836             else:
837                 lst.append (i)
838     return lst
839
840 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
841     subdirs = flatten (cvs_dirs ('.'), [])
842 else:
843     # ugh
844     command = 'cd %(srcdir)s \
845     && find . -name SConscript | sed s@/SConscript@@' % vars ()
846     subdirs = string.split (os.popen (command).read ())
847
848 if env['fast']\
849    and 'all' not in COMMAND_LINE_TARGETS\
850    and 'doc' not in COMMAND_LINE_TARGETS\
851    and 'web' not in COMMAND_LINE_TARGETS\
852    and 'install' not in COMMAND_LINE_TARGETS\
853    and 'clean' not in COMMAND_LINE_TARGETS:
854     subdirs = [ 'python',
855             'lily',
856            'flower',
857            'mf',
858            ]
859
860 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
861     src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
862 else:
863     src_files = ['foobar']
864
865 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
866 txt_files = map (lambda x: x + '.txt', readme_files)
867
868
869 #
870 # speeds up build by +- 5% 
871
872 if not env['fast']:
873     foo = map (lambda x: env.TXT (x + '.txt',
874                       os.path.join ('Documentation/topdocs', x)),
875            readme_files)
876     tar_base = package.name + '-' + version
877     tar_name = tar_base + '.tar.gz'
878     ball_prefix = os.path.join (outdir, tar_base)
879     tar_ball = os.path.join (outdir, tar_name)
880
881     dist_files = src_files + txt_files
882     ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
883     map (lambda x: env.Depends (tar_ball, x), ball_files)
884     map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
885                     'ln $SOURCE $TARGET'), dist_files)
886     tar = env.Command (tar_ball, src_files,
887                ['rm -f $$(find $TARGET.dir -name .sconsign)',
888                 'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
889     env.Alias ('tar', tar)
890
891     dist_ball = os.path.join (package.release_dir, tar_name)
892     env.Command (dist_ball, tar_ball,
893              'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
894              + 'ln $SOURCE $TARGET')
895     env.Depends ('dist', dist_ball)
896     patch_name = os.path.join (outdir, tar_base + '.diff.gz')
897     patch = env.PATCH (patch_name, tar_ball)
898     env.Depends (patch_name, dist_ball)
899     env.Alias ('release', patch)
900
901 #### web
902 if not env['fast']:
903     web_base = os.path.join (outdir, 'web')
904     web_ball = web_base + '.tar.gz'
905     env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
906     web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
907     web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
908     env['web_path'] = web_path
909     web_list = os.path.join (outdir, 'weblist')
910     # compatible make heritits
911     # fixme: generate in $outdir is cwd/builddir
912     env.Command (web_list,
913              ## Adding 'doc' dependency is correct, but takes
914              ## > 5min extra if you have a peder :-)
915              #'doc',
916              
917              '#/VERSION',
918              ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
919               'cd $absbuild && $footify $$(find . -name "*.html" -print)',
920               'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
921               'cd $absbuild && find Documentation input $web_path \
922               > $TARGET',
923               '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
924               '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
925               'cd $absbuild && ls *.html >> $TARGET',])
926     env.Command (web_ball, web_list,
927              ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
928     #env.Alias ('web', web_ball)
929     www_base = os.path.join (outdir, 'www')
930     www_ball = www_base + '.tar.gz'
931     env.Command (www_ball, web_ball,
932              ['rm -rf $out/tmp',
933               'mkdir -p $absbuild/$out/tmp',
934               'tar -C $absbuild/$out/tmp -xzf $SOURCE',
935               'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
936               + ' do mv $$i $$(dirname $$i)/out-www; done',
937               'tar -C $absbuild/$out/tmp -czf $TARGET .'])
938     env.Alias ('web', www_ball)
939
940 #### tags
941 env.Append (
942     ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
943     --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
944 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
945 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
946                  src_files),
947          'etags $ETAGSFLAGS $SOURCES')
948
949 # Note: SConscripts are only needed in directories where something needs
950 # to be done, building or installing
951 for d in subdirs:
952     if os.path.exists (os.path.join (d, 'SConscript')):
953         b = os.path.join (env['build'], d, env['out'])
954         # Support clean sourcetree build (--srcdir build)
955         # and ./out build.
956         if os.path.abspath (b) != os.path.abspath (d):
957             env.BuildDir (b, d, duplicate = 0)
958         SConscript (os.path.join (b, 'SConscript'))
959
960 env.Command ('tree', ['#/VERSION', '#/SConstruct'], symlink_tree)