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