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