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