Update from Andrew: tweaks for cross-staff chords snippet.
[lilypond.git] / SConstruct
blob6ec14e2274343aaa9aea12cab07c8a56fa1947be
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 'PERL',
138 'PYTHON',
139 'SH',
142 # Put your favourite stuff in custom.py
143 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
144 opts.Add ('prefix', 'Install prefix', '/usr/')
145 opts.Add ('out', 'Output directory', 'out-scons')
146 opts.Add ('build', 'Build directory', '.')
147 opts.Add ('DESTDIR', 'DESTDIR prepended to prefix', '')
148 opts.AddOptions (
149 BoolOption ('warnings', 'compile with -Wall and similiar',
151 BoolOption ('debugging', 'compile with debugging symbols',
153 BoolOption ('optimising', 'compile with optimising',
155 BoolOption ('shared', 'build shared libraries',
157 BoolOption ('static', 'build static libraries',
159 BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
161 BoolOption ('verbose', 'run commands with verbose flag',
163 BoolOption ('checksums', 'use checksums instead of timestamps',
165 BoolOption ('fast', 'use timestamps, implicit cache, prune CPPPATH',
169 srcdir = Dir ('.').srcnode ().abspath
170 #ugh
171 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
173 try:
174 import packagepython
175 packagepython.Package (srcdir)
176 packagepython.version_tuple_to_str (package.version)
177 except:
178 print '*** FIXME: no packagepython. setting version to 1.0'
179 class Package:
180 name = 'lilypond'
181 release_dir = '.'
182 package = Package
183 version = '1.0'
185 ENV = { 'PYTHONPATH': '' }
186 for key in ['GUILE_LOAD_PATH', 'LD_LIBRARY_PATH', 'PATH', 'PKG_CONFIG_PATH',
187 'PYTHONPATH', 'TEXMF']:
188 if os.environ.has_key (key):
189 ENV[key] = os.environ[key]
191 ENV['PYTHONPATH'] = os.path.join (srcdir, 'python') + ':' + ENV['PYTHONPATH']
193 env = Environment (
194 ENV = ENV,
195 BYTEORDER = sys.byteorder.upper (),
196 CC = '$GCC',
197 CXX = '$GXX',
198 CPPDEFINES = '-DHAVE_CONFIG_H',
199 MAKEINFO = 'LANG= makeinfo',
200 MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
202 PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
203 'usr/pkg/gnome/lib'),
204 os.path.join (os.environ['HOME'],
205 'usr/pkg/pango/lib')],
206 GZIP='-9v',
207 MFMODE = 'ljfour',
208 TOPLEVEL_VERSION = version,
211 Help (usage + opts.GenerateHelpText (env))
213 # Add all config_vars to opts, so that they will be read and saved
214 # together with the other configure options.
215 map (lambda x: opts.AddOptions ((x,)), config_vars)
216 opts.Update (env)
218 for key in config_vars:
219 if os.environ.has_key (key):
220 env[key] = os.environ[key]
222 if env['fast']:
223 # Usability switch (Anthony Roach).
224 # See http://www.scons.org/cgi-bin/wiki/GoFastButton
225 # First do: scons realclean .
226 env['checksums'] = 0
227 SetOption ('max_drift', 1)
228 SetOption ('implicit_cache', 1)
229 elif env['checksums']:
230 # Always use checksums (makes more sense than timestamps).
231 SetOption ('max_drift', 0)
232 # Using *content* checksums prevents rebuilds after
233 # [re]configure if config.hh has not changed. Too bad that it
234 # is unusably slow.
235 TargetSignatures ('content')
237 absbuild = Dir (env['build']).abspath
238 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
239 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
242 config_hh = os.path.join (outdir, 'config.hh')
243 version_hh = os.path.join (outdir, 'version.hh')
245 env.Alias ('config', config_cache)
247 cachedir = os.path.join (outdir, 'build-cache')
249 if not os.path.exists (cachedir):
250 os.makedirs (cachedir)
252 CacheDir (cachedir)
254 # No need to set $LILYPOND_DATADIR to run lily, but cannot install...
255 if env['debugging'] and not 'install' in COMMAND_LINE_TARGETS:
256 env['prefix'] = run_prefix
258 prefix = env['prefix']
259 bindir = os.path.join (prefix, 'bin')
260 sharedir = os.path.join (prefix, 'share')
261 libdir = os.path.join (prefix, 'lib')
262 libdir_package = os.path.join (libdir, package.name)
263 libdir_package_version = os.path.join (libdir_package, version)
264 localedir = os.path.join (sharedir, 'locale')
265 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
266 sharedir_package = os.path.join (sharedir, package.name)
267 sharedir_package_version = os.path.join (sharedir_package, version)
268 lilypondprefix = sharedir_package_version
270 # junkme
271 env.Append (
272 absbuild = absbuild,
273 srcdir = srcdir,
278 def symlink_tree (target, source, env):
279 def mkdirs (dir):
280 def mkdir (dir):
281 if not dir:
282 os.chdir (os.sep)
283 return
284 if not os.path.isdir (dir):
285 if os.path.exists (dir):
286 os.unlink (dir)
287 os.mkdir (dir)
288 os.chdir (dir)
289 map (mkdir, string.split (dir, os.sep))
290 def symlink (src, dst):
291 os.chdir (absbuild)
292 dir = os.path.dirname (dst)
293 mkdirs (dir)
294 if src[0] == '#':
295 frm = os.path.join (srcdir, src[1:])
296 else:
297 depth = len (string.split (dir, '/'))
298 if src.find ('@') > -1:
299 frm = os.path.join ('../' * depth,
300 string.replace (src, '@',
301 env['out']))
302 else:
303 frm = os.path.join ('../' * depth, src,
304 env['out'])
305 if src[-1] == '/':
306 frm = os.path.join (frm, os.path.basename (dst))
307 if env['verbose']:
308 print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
309 os.symlink (frm, os.path.basename (dst))
310 shutil.rmtree (run_prefix)
311 prefix = os.path.join (env['out'], 'usr')
312 map (lambda x: symlink (x[0], os.path.join (prefix,
313 x[1] % {'ver' : version})),
314 # ^# := source dir
315 # @ := out
316 # /$ := add dst file_name
317 (('python', 'lib/lilypond/python'),
318 # ugh
319 ('python', 'share/lilypond/%(ver)s/python'),
320 ('lily/', 'bin/lilypond'),
321 ('scripts/', 'bin/convert-ly'),
322 ('scripts/', 'bin/lilypond-book'),
323 ('scripts/', 'bin/ps2png'),
324 ('mf', 'share/lilypond/%(ver)s/dvips/mf-out'),
325 ('#ps/music-drawing-routines.ps',
326 'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
327 ('mf', 'share/lilypond/%(ver)s/otf'),
328 ('mf', 'share/lilypond/%(ver)s/tfm'),
329 ('tex', 'share/lilypond/%(ver)s/tex/enc'),
330 ('#mf', 'share/lilypond/%(ver)s/fonts/mf'),
331 ('mf', 'share/lilypond/%(ver)s/fonts/map'),
332 ('mf', 'share/lilypond/%(ver)s/fonts/otf'),
333 ('mf', 'share/lilypond/%(ver)s/fonts/tfm'),
334 ('mf', 'share/lilypond/%(ver)s/fonts/type1'),
335 ('#tex', 'share/lilypond/%(ver)s/tex/source'),
336 ('tex', 'share/lilypond/%(ver)s/tex/tex-out'),
337 ('mf', 'share/lilypond/%(ver)s/tex/mf-out'),
338 ('#ly', 'share/lilypond/%(ver)s/ly'),
339 ('#scm', 'share/lilypond/%(ver)s/scm'),
340 ('#scripts', 'share/lilypond/%(ver)s/scripts'),
341 ('#ps', 'share/lilypond/%(ver)s/ps'),
342 ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
343 ('elisp', 'share/lilypond/%(ver)s/elisp')))
345 print "FIXME: BARF BARF BARF"
346 os.chdir (absbuild)
347 out = env['out']
348 ver = version
349 prefix = os.path.join (env['out'], 'usr/share/lilypond/%(ver)s/fonts'
350 % vars ())
351 for ext in ('enc', 'map', 'otf', 'svg', 'tfm', 'pfa'):
352 dir = os.path.join (absbuild, prefix, ext)
353 os.system ('rm -f ' + dir)
354 mkdirs (dir)
355 os.chdir (dir)
356 os.system ('ln -s ../../../../../../../mf/%(out)s/*.%(ext)s .'
357 % vars ())
358 os.chdir (srcdir)
360 def configure (target, source, env):
361 dre = re.compile ('\n(200[0-9]{5})')
362 vre = re.compile ('.*?\n[^-.0-9]*([0-9][0-9]*\.[0-9]([.0-9]*[0-9])*)',
363 re.DOTALL)
364 def get_version (program):
365 command = '(pkg-config --modversion %(program)s || %(program)s --version || %(program)s -V) 2>&1' % vars ()
366 pipe = os.popen (command)
367 output = pipe.read ()
368 if pipe.close ():
369 return None
370 splits = re.sub ('^|\s', '\n', output)
371 date_hack = re.sub (dre, '\n0.0.\\1', splits)
372 m = re.match (vre, date_hack)
373 v = m.group (1)
374 if v[-1] == '\n':
375 v = v[:-1]
376 return string.split (v, '.')
378 def test_version (lst, full_name, minimal, description, package):
379 program = os.path.basename (full_name)
380 sys.stdout.write ('Checking %s version... ' % program)
381 actual = get_version (program)
382 if not actual:
383 print 'not found'
384 lst.append ((description, package, minimal, program,
385 'not installed'))
386 return 0
387 print string.join (actual, '.')
388 if map (string.atoi, actual) \
389 < map (string.atoi, string.split (minimal, '.')):
390 lst.append ((description, package, minimal, program,
391 string.join (actual, '.')))
392 return 0
393 return 1
395 def test_program (lst, program, minimal, description, package):
396 key = program.upper ()
397 if key.find ('+-'):
398 key = re.sub ('\+', 'X', key)
399 key = re.sub ('-', '_', key)
400 sys.stdout.write ('Checking for %s ... ' % program)
401 if env.has_key (key):
402 f = env[key]
403 sys.stdout.write ('(cached) ')
404 else:
405 f = WhereIs (program)
406 env[key] = f
407 if not f:
408 print 'not found'
409 lst.append ((description, package, minimal, program,
410 'not installed'))
411 return 0
412 print f
413 return test_version (lst, program, minimal, description, package)
415 def test_lib (lst, program, minimal, description, package):
416 # FIXME: test for Debian or RPM (or -foo?) based dists
417 # to guess (or get correct!: apt-cache search?)
418 # package name.
419 #if os.system ('pkg-config --atleast-version=0 freetype2'):
420 # barf
421 if test_version (lst, program, minimal, description,
422 'lib%(package)s-dev or %(package)s-devel'
423 % vars ()):
424 env.ParseConfig ('pkg-config --cflags --libs %(program)s'
425 % vars ())
426 return 1
427 return 0
429 required = []
430 test_program (required, 'bash', '2.0', 'Bash', 'bash')
431 test_program (required, 'gcc', '4.0', 'GNU C compiler', 'gcc')
432 test_program (required, 'g++', '4.0.5', 'GNU C++ compiler', 'g++')
433 test_program (required, 'guile-config', '1.8', 'GUILE development',
434 'libguile-dev or guile-devel')
435 test_program (required, 'mf', '0.0', 'Metafont', 'tetex-bin')
436 test_program (required, 'python', '2.1', 'Python (www.python.org)',
437 'python')
438 # Silly, and breaks with /bin/sh == dash
439 #test_program (required, 'sh', '0.0', 'Bourne shell', 'sh')
441 optional = []
442 # Do not use bison 1.50 and 1.75.
443 #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
444 test_program (optional, 'bison', '1.25', 'Bison -- parser generator',
445 'bison')
446 test_program (optional, 'fontforge', '0.0.20050624', 'FontForge',
447 'fontforge')
448 test_program (optional, 'flex', '0.0', 'Flex -- lexer generator',
449 'flex')
450 test_program (optional, 'guile', '1.8', 'GUILE scheme', 'guile')
451 test_program (optional, 'gs', '8.15',
452 'Ghostscript PostScript interpreter',
453 'gs or gs-afpl or gs-esp or gs-gpl')
454 test_program (optional, 'makeinfo', '4.8', 'Makeinfo tool', 'texinfo')
455 test_program (optional, 'perl', '4.0',
456 'Perl practical efficient readonly language', 'perl')
458 def CheckYYCurrentBuffer (context):
459 context.Message ('Checking for yy_current_buffer... ')
460 ret = conf.TryCompile ("""using namespace std;
461 #include <FlexLexer.h>
462 class yy_flex_lexer: public yyFlexLexer
464 public:
465 yy_flex_lexer ()
467 yy_current_buffer = 0;
469 };""", '.cc')
470 context.Result (ret)
471 return ret
473 conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
474 : CheckYYCurrentBuffer })
476 defines = {
477 'DIRSEP' : "'%s'" % os.sep,
478 'PATHSEP' : "'%s'" % os.pathsep,
479 'PACKAGE': '"%s"' % package.name,
480 'DATADIR' : '"%s"' % sharedir,
481 'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
482 'LOCALEDIR' : '"%s"' %localedir,
484 conf.env.Append (DEFINES = defines)
486 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
487 PYTHON_INCLUDE = os.popen (command).read ()#[:-1]
488 if env['fast']:
489 env.Append (CCFLAGS = ['-I%s' % PYTHON_INCLUDE])
490 else:
491 env.Append (CPPPATH = [PYTHON_INCLUDE])
493 headers = ('assert.h', 'grp.h', 'libio.h', 'pwd.h',
494 'sys/stat.h', 'utf8/wchar.h', 'wchar.h', 'Python.h')
495 for i in headers:
496 if conf.CheckCHeader (i):
497 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
498 conf.env['DEFINES'][key] = 1
500 ccheaders = ('sstream',)
501 for i in ccheaders:
502 if conf.CheckCXXHeader (i):
503 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
504 conf.env['DEFINES'][key] = 1
506 functions = ('chroot', 'fopencookie', 'funopen',
507 'gettext', 'isinf',
508 'mbrtowc', 'memmem', 'snprintf', 'vsnprintf', 'wcrtomb')
509 for i in functions:
510 if 0 or conf.CheckFunc (i):
511 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
512 conf.env['DEFINES'][key] = 1
514 if conf.CheckYYCurrentBuffer ():
515 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
517 if conf.CheckLib ('dl'):
518 pass
520 if env['fast']:
521 cpppath = []
522 if env.has_key ('CPPPATH'):
523 cpppath = env['CPPPATH']
525 ## FIXME: linkage, check for libguile.h and scm_boot_guile
526 #this could happen after flower...
527 env.ParseConfig ('guile-config compile')
529 test_program (required, 'pkg-config', '0.9.0',
530 'pkg-config library compile manager', 'pkg-config')
531 if test_lib (required, 'freetype2', '0.0',
532 'Development files for FreeType 2 font engine',
533 'freetype6'):
534 conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
536 if test_lib (required, 'pangoft2', '1.6.0',
537 'Development files for pango, with FreeType2',
538 'pango1.0'):
539 conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
541 if test_lib (optional, 'fontconfig', '2.2.0',
542 'Development files for fontconfig', 'fontconfig1'):
543 conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
545 #this could happen only for compiling pango-*
546 if env['gui']:
547 test_lib (required, 'gtk+-2.0', '2.4.0',
548 'Development files for GTK+', 'gtk2.0')
550 if env['fast']:
551 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
552 # <system-dir>] speeds up SCons
553 env['CCFLAGS'] += map (lambda x: '-I' + x,
554 env['CPPPATH'][len (cpppath):])
555 env['CPPPATH'] = cpppath
557 if required:
558 print
559 print '********************************'
560 print 'Please install required packages'
561 for i in required:
562 print '%s: %s-%s or newer (found: %s %s)' % i
563 Exit (1)
565 if optional:
566 print
567 print '*************************************'
568 print 'Consider installing optional packages'
569 for i in optional:
570 print '%s: %s-%s or newer (found: %s %s)' % i
572 return conf.Finish ()
574 def config_header (target, source, env):
575 config = open (str (target[0]), 'w')
576 for i in sorted (env['DEFINES'].keys ()):
577 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
578 config.close ()
579 env.Command (config_hh, config_cache, config_header)
581 # hmm?
582 def xuniquify (lst):
583 n = []
584 for i in lst:
585 if not i in n:
586 n.append (i)
587 lst = n
588 return lst
590 def uniquify (lst):
591 d = {}
592 n = len (lst)
593 i = 0
594 while i < n:
595 if not d.has_key (lst[i]):
596 d[lst[i]] = 1
597 i += 1
598 else:
599 del lst[i]
600 n -= 1
601 return lst
603 def uniquify_config_vars (env):
604 for i in config_vars:
605 if env.has_key (i) and type (env[i]) == type ([]):
606 env[i] = uniquify (env[i])
608 def save_config_cache (env):
609 ## FIXME: Is this smart, using option cache for saving
610 ## config.cache? I cannot seem to find the official method.
611 uniquify_config_vars (env)
612 opts.Save (config_cache, env)
614 if 'config' in COMMAND_LINE_TARGETS:
615 sys.stdout.write ('\n')
616 sys.stdout.write ('LilyPond configured')
617 sys.stdout.write ('\n')
618 sys.stdout.write ('Now run')
619 sys.stdout.write ('\n')
620 sys.stdout.write (' scons [TARGET|DIR]...')
621 sys.stdout.write ('\n')
622 sys.stdout.write ('\n')
623 sys.stdout.write ('Examples:')
624 sys.stdout.write ('\n')
625 sys.stdout.write (' scons lily # build lilypond')
626 sys.stdout.write ('\n')
627 sys.stdout.write (' scons all # build everything')
628 sys.stdout.write ('\n')
629 sys.stdout.write (' scons doc # build documentation')
630 sys.stdout.write ('\n')
631 ## TODO
632 ## sys.stdout.write (' scons prefix=/usr DESTDIR=/tmp/pkg all install')
633 ## sys.stdout.write ('\n')
634 Exit (0)
635 elif not env['checksums']:
636 # When using timestams, config.hh is NEW. The next
637 # build triggers recompilation of everything. Exiting
638 # here makes SCons use the actual timestamp for config.hh
639 # and prevents recompiling everything the next run.
640 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
641 sys.stdout.write ('Running %s ... ' % command)
642 sys.stdout.write ('\n')
643 s = os.system (command)
644 Exit (s)
646 # WTF?
647 # scons: *** Calling Configure from Builders is not supported.
648 # env.Command (config_cache, None, configure)
649 if not os.path.exists (config_cache) \
650 or (os.stat ('SConstruct')[stat.ST_MTIME]
651 > os.stat (config_cache)[stat.ST_MTIME]):
652 env = configure (None, None, env)
653 save_config_cache (env)
654 elif env['checksums']:
655 # just save everything
656 save_config_cache (env)
658 #urg how does #/ subst work?
659 Export ('env')
660 SConscript ('buildscripts/builder.py')
662 env.PrependENVPath ('PATH',
663 os.path.join (env['absbuild'], env['out'], 'usr/bin'))
665 LILYPOND_DATADIR = os.path.join (run_prefix, 'share/lilypond/', version)
667 if not os.path.exists (LILYPOND_DATADIR):
668 os.makedirs (LILYPOND_DATADIR)
670 env.Command (LILYPOND_DATADIR, ['#/SConstruct', '#/VERSION'], symlink_tree)
671 env.Depends ('lily', LILYPOND_DATADIR)
673 env.Append (ENV = {
674 'LILYPOND_DATADIR' : LILYPOND_DATADIR,
675 'TEXMF' : '{$LILYPOND_DATADIR,'
676 + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
679 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
680 BUILD_LILYPOND = '$absbuild/lily/$out/lilypond ${__verbose}'
681 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py ${__verbose}'
683 if env['verbose'] and env['verbose'] != '0':
684 env['__verbose'] = ' --verbose'
685 env['set__x'] = 'set -x;'
687 # post-option environment-update
688 env.Append (
689 bindir = bindir,
690 sharedir = sharedir,
691 lilypond_datadir = sharedir_package,
692 localedir = localedir,
693 local_lilypond_datadir = sharedir_package_version,
694 lilypondprefix = lilypondprefix,
695 sharedir_package = sharedir_package,
696 sharedir_doc_package = sharedir_doc_package,
697 sharedir_package_version = sharedir_package_version,
698 libdir_package = libdir_package,
699 libdir_package_version = libdir_package_version,
701 LILYPOND = BUILD_LILYPOND,
702 ABC2LY = BUILD_ABC2LY,
703 LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
704 LILYPOND_BOOK_FORMAT = 'texi-html',
705 MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
708 env.Append (CCFLAGS = ['-pipe', '-Wno-pmf-conversions'])
709 if env['debugging']:
710 env.Append (CCFLAGS = ['-g'])
711 if env['optimising']:
712 env.Append (CCFLAGS = '-O2')
713 if env['warnings']:
714 env.Append (CCFLAGS = ['-W', '-Wall'])
715 env.Append (CXXFLAGS = ['-Wconversion'])
717 # ugr,huh?
718 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
719 # FIXME: ParseConfig ignores -L flag?
720 env.Append (LINKFLAGS = ['-L/usr/X11R6/lib'])
722 ## Explicit target and dependencies
724 if 'clean' in COMMAND_LINE_TARGETS:
725 # ugh: prevent reconfigure instead of clean
726 os.system ('touch %s' % config_cache)
728 command = sys.argv[0] + ' -c .'
729 sys.stdout.write ('Running %s ... ' % command)
730 sys.stdout.write ('\n')
731 s = os.system (command)
732 if os.path.exists (config_cache):
733 os.unlink (config_cache)
734 Exit (s)
736 if 'sconsclean' in COMMAND_LINE_TARGETS:
737 command = 'rm -rf scons.cache $(find . -name ".scon*")'
738 s = os.system (command)
739 if os.path.exists (config_cache):
740 os.unlink (config_cache)
741 Exit (s)
743 if 'realclean' in COMMAND_LINE_TARGETS:
744 command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
745 sys.stdout.write ('Running %s ... ' % command)
746 sys.stdout.write ('\n')
747 s = os.system (command)
748 if os.path.exists (config_cache):
749 os.unlink (config_cache)
750 Exit (s)
752 # Declare SConscript phonies
753 env.Alias ('minimal', config_cache)
755 if 0:
756 env.Alias ('mf-essential', config_cache)
757 env.Alias ('minimal', ['python', 'lily', 'mf-essential'])
758 env.Alias ('all', ['minimal', 'mf', '.'])
760 else:
761 env.Alias ('minimal', ['python', 'lily', 'mf'])
762 env.Alias ('all', ['minimal', '.'])
765 # Do we want the doc/web separation?
766 env.Alias ('doc',
767 ['minimal',
768 'Documentation',
769 'Documentation/user',
770 'Documentation/topdocs',
771 'Documentation/bibliography',
772 'input'])
774 # Without target arguments, do minimal build
775 if not COMMAND_LINE_TARGETS:
776 env.Default (['minimal'])
778 # GNU Make rerouting compat:
779 env.Alias ('web', 'doc')
782 env.Command (version_hh, '#/VERSION',
783 '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
785 # post-config environment update
786 env.Append (
787 run_prefix = run_prefix,
788 LILYPOND_DATADIR = LILYPOND_DATADIR,
790 # FIXME: move to lily/SConscript?
791 LIBPATH = [os.path.join (absbuild, 'flower', env['out'])],
792 CPPPATH = [outdir, ],
793 LILYPOND_PATH = ['.',
794 '$srcdir/input',
795 '$srcdir/input/regression',
796 '$srcdir/input/test',
797 '$srcdir/input/tutorial',
798 '$srcdir/Documentation/user',
799 '$absbuild/mf/$out',
800 # os.path.join (absbuild, 'Documentation',
801 # env['out']),
802 # os.path.join (absbuild, 'Documentation/user',
803 # env['out']),
805 MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
806 '$absbuild/Documentation/user/$out'],
809 #### dist, tar
810 def plus (a, b):
811 a + b
813 def cvs_entry_is_dir (line):
814 return line[0] == 'D' and line[-2] == '/'
816 def cvs_entry_is_file (line):
817 return line[0] == '/' and line[-2] == '/'
819 def cvs_dirs (dir):
820 entries = os.path.join (dir, 'CVS/Entries')
821 if not os.path.exists (entries):
822 return []
823 entries = open (entries).readlines ()
824 dir_entries = filter (cvs_entry_is_dir, entries)
825 dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
826 dir_entries)
827 return dirs + map (cvs_dirs, dirs)
829 def cvs_files (dir):
830 entries = os.path.join (dir, 'CVS/Entries')
831 if not os.path.exists (entries):
832 return []
833 entries = open (entries).readlines ()
834 file_entries = filter (cvs_entry_is_file, entries)
835 files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
836 return map (lambda x: os.path.join (dir, x), files)
838 def flatten (tree, lst):
839 if type (tree) == type ([]):
840 for i in tree:
841 if type (i) == type ([]):
842 flatten (i, lst)
843 else:
844 lst.append (i)
845 return lst
847 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
848 subdirs = flatten (cvs_dirs ('.'), [])
849 else:
850 # ugh
851 command = 'cd %(srcdir)s \
852 && find . -name SConscript | sed s@/SConscript@@' % vars ()
853 subdirs = string.split (os.popen (command).read ())
855 if env['fast']\
856 and 'all' not in COMMAND_LINE_TARGETS\
857 and 'doc' not in COMMAND_LINE_TARGETS\
858 and 'web' not in COMMAND_LINE_TARGETS\
859 and 'install' not in COMMAND_LINE_TARGETS\
860 and 'clean' not in COMMAND_LINE_TARGETS:
861 subdirs = [ 'python',
862 'lily',
863 'flower',
864 'mf',
867 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
868 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
869 else:
870 src_files = ['foobar']
872 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
873 txt_files = map (lambda x: x + '.txt', readme_files)
877 # speeds up build by +- 5%
879 if not env['fast']:
880 foo = map (lambda x: env.TXT (x + '.txt',
881 os.path.join ('Documentation/topdocs', x)),
882 readme_files)
883 tar_base = package.name + '-' + version
884 tar_name = tar_base + '.tar.gz'
885 ball_prefix = os.path.join (outdir, tar_base)
886 tar_ball = os.path.join (outdir, tar_name)
888 dist_files = src_files + txt_files
889 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
890 map (lambda x: env.Depends (tar_ball, x), ball_files)
891 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
892 'ln $SOURCE $TARGET'), dist_files)
893 tar = env.Command (tar_ball, src_files,
894 ['rm -f $$(find $TARGET.dir -name .sconsign)',
895 'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
896 env.Alias ('tar', tar)
898 dist_ball = os.path.join (package.release_dir, tar_name)
899 env.Command (dist_ball, tar_ball,
900 'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
901 + 'ln $SOURCE $TARGET')
902 env.Depends ('dist', dist_ball)
903 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
904 patch = env.PATCH (patch_name, tar_ball)
905 env.Depends (patch_name, dist_ball)
906 env.Alias ('release', patch)
908 #### web
909 if not env['fast']:
910 web_base = os.path.join (outdir, 'web')
911 web_ball = web_base + '.tar.gz'
912 env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
913 web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
914 web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
915 env['web_path'] = web_path
916 web_list = os.path.join (outdir, 'weblist')
917 # compatible make heritits
918 # fixme: generate in $outdir is cwd/builddir
919 env.Command (web_list,
920 ## Adding 'doc' dependency is correct, but takes
921 ## > 5min extra if you have a peder :-)
922 #'doc',
924 '#/VERSION',
925 ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
926 'cd $absbuild && $footify $$(find . -name "*.html" -print)',
927 'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
928 'cd $absbuild && find Documentation input $web_path \
929 > $TARGET',
930 '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
931 '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
932 'cd $absbuild && ls *.html >> $TARGET',])
933 env.Command (web_ball, web_list,
934 ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
935 #env.Alias ('web', web_ball)
936 www_base = os.path.join (outdir, 'www')
937 www_ball = www_base + '.tar.gz'
938 env.Command (www_ball, web_ball,
939 ['rm -rf $out/tmp',
940 'mkdir -p $absbuild/$out/tmp',
941 'tar -C $absbuild/$out/tmp -xzf $SOURCE',
942 'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
943 + ' do mv $$i $$(dirname $$i)/out-www; done',
944 'tar -C $absbuild/$out/tmp -czf $TARGET .'])
945 env.Alias ('web', www_ball)
947 #### tags
948 env.Append (
949 ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
950 --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
951 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
952 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
953 src_files),
954 'etags $ETAGSFLAGS $SOURCES')
956 # Note: SConscripts are only needed in directories where something needs
957 # to be done, building or installing
958 for d in subdirs:
959 if os.path.exists (os.path.join (d, 'SConscript')):
960 b = os.path.join (env['build'], d, env['out'])
961 # Support clean sourcetree build (--srcdir build)
962 # and ./out build.
963 if os.path.abspath (b) != os.path.abspath (d):
964 env.BuildDir (b, d, duplicate = 0)
965 SConscript (os.path.join (b, 'SConscript'))
967 env.Command ('tree', ['#/VERSION', '#/SConstruct'], symlink_tree)