*** empty log message ***
[lilypond.git] / SConstruct
blobd4c847f046c6834d1fbb2f8ce8a683b86f6bfe9d
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, 95)
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'))
173 import packagepython
174 package = packagepython.Package (srcdir)
175 version = packagepython.version_tuple_to_str (package.version)
177 ENV = { 'PATH' : os.environ['PATH'] }
178 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH', 'TEXMF']:
179 if os.environ.has_key (key):
180 ENV[key] = os.environ[key]
182 env = Environment (
183 ENV = ENV,
184 BYTEORDER = sys.byteorder.upper (),
185 CC = '$GCC',
186 CXX = '$GXX',
187 CPPDEFINES = '-DHAVE_CONFIG_H',
188 MAKEINFO = 'LANG= makeinfo',
189 MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
191 PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
192 'usr/pkg/gnome/lib'),
193 os.path.join (os.environ['HOME'],
194 'usr/pkg/pango/lib')],
195 GZIP='-9v',
196 MFMODE = 'ljfour',
197 TOPLEVEL_VERSION = version,
200 Help (usage + opts.GenerateHelpText (env))
202 # Add all config_vars to opts, so that they will be read and saved
203 # together with the other configure options.
204 map (lambda x: opts.AddOptions ((x,)), config_vars)
205 opts.Update (env)
207 for key in config_vars:
208 if os.environ.has_key (key):
209 env[key] = os.environ[key]
211 if env['fast']:
212 # Usability switch (Anthony Roach).
213 # See http://www.scons.org/cgi-bin/wiki/GoFastButton
214 # First do: scons realclean .
215 env['checksums'] = 0
216 SetOption ('max_drift', 1)
217 SetOption ('implicit_cache', 1)
218 elif env['checksums']:
219 # Always use checksums (makes more sense than timestamps).
220 SetOption ('max_drift', 0)
221 # Using *content* checksums prevents rebuilds after
222 # [re]configure if config.hh has not changed. Too bad that it
223 # is unusably slow.
224 TargetSignatures ('content')
226 absbuild = Dir (env['build']).abspath
227 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
228 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
231 config_hh = os.path.join (outdir, 'config.hh')
232 version_hh = os.path.join (outdir, 'version.hh')
234 env.Alias ('config', config_cache)
236 cachedir = os.path.join (outdir, 'build-cache')
238 if not os.path.exists (cachedir):
239 os.makedirs (cachedir)
241 CacheDir (cachedir)
243 # No need to set $LILYPONDPREFIX to run lily, but cannot install...
244 if env['debugging'] and not 'install' in COMMAND_LINE_TARGETS:
245 env['prefix'] = run_prefix
247 prefix = env['prefix']
248 bindir = os.path.join (prefix, 'bin')
249 sharedir = os.path.join (prefix, 'share')
250 libdir = os.path.join (prefix, 'lib')
251 localedir = os.path.join (sharedir, 'locale')
252 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
253 sharedir_package = os.path.join (sharedir, package.name)
254 sharedir_package_version = os.path.join (sharedir_package, version)
255 lilypondprefix = sharedir_package_version
257 # junkme
258 env.Append (
259 absbuild = absbuild,
260 srcdir = srcdir,
264 def list_sort (lst):
265 sorted = lst
266 sorted.sort ()
267 return sorted
270 def configure (target, source, env):
271 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9]([.0-9]*[0-9])*).*$',
272 re.DOTALL)
273 def get_version (program):
274 command = '(pkg-config --modversion %(program)s || %(program)s --version || %(program)s -V) 2>&1' % vars ()
275 pipe = os.popen (command)
276 output = pipe.read ()
277 if pipe.close ():
278 return None
279 v = re.sub (vre, '\\1', output)
280 if v[-1] == '\n':
281 v = v[:-1]
282 return string.split (v, '.')
284 def test_version (lst, full_name, minimal, description, package):
285 program = os.path.basename (full_name)
286 sys.stdout.write ('Checking %s version... ' % program)
287 actual = get_version (program)
288 if not actual:
289 print 'not found'
290 lst.append ((description, package, minimal, program,
291 'not installed'))
292 return 0
293 print string.join (actual, '.')
294 if map (string.atoi, actual) \
295 < map (string.atoi, string.split (minimal, '.')):
296 lst.append ((description, package, minimal, program,
297 string.join (actual, '.')))
298 return 0
299 return 1
301 def test_program (lst, program, minimal, description, package):
302 key = program.upper ()
303 if key.find ('+-'):
304 key = re.sub ('\+', 'X', key)
305 key = re.sub ('-', '_', key)
306 sys.stdout.write ('Checking for %s ... ' % program)
307 if env.has_key (key):
308 f = env[key]
309 sys.stdout.write ('(cached) ')
310 else:
311 f = WhereIs (program)
312 env[key] = f
313 if not f:
314 print 'not found'
315 lst.append ((description, package, minimal, program,
316 'not installed'))
317 return 0
318 print f
319 return test_version (lst, program, minimal, description, package)
321 def test_lib (lst, program, minimal, description, package):
322 # FIXME: test for Debian or RPM (or -foo?) based dists
323 # to guess (or get correct!: apt-cache search?)
324 # package name.
325 #if os.system ('pkg-config --atleast-version=0 freetype2'):
326 # barf
327 if test_version (lst, program, minimal, description,
328 'lib%(package)s-dev or %(package)s-devel'
329 % vars ()):
330 env.ParseConfig ('pkg-config --cflags --libs %(program)s'
331 % vars ())
332 return 1
333 return 0
335 required = []
336 test_program (required, 'bash', '2.0', 'Bash', 'bash')
337 test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
338 test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
339 test_program (required, 'guile-config', '1.6', 'GUILE development',
340 'libguile-dev or guile-devel')
341 test_program (required, 'mf', '0.0', 'Metafont', 'tetex-bin')
342 test_program (required, 'mftrace', '1.1.9',
343 'mftrace (http://xs4all.nl/~hanwen/mftrace)', 'mftrace')
344 test_program (required, 'potrace', '0.0', 'Potrace', 'potrace')
345 test_program (required, 'python', '2.1', 'Python (www.python.org)',
346 'python')
347 test_program (required, 'sh', '0.0', 'Bourne shell', 'sh')
349 optional = []
350 # Do not use bison 1.50 and 1.75.
351 #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
352 test_program (optional, 'bison', '1.25', 'Bison -- parser generator',
353 'bison')
354 test_program (optional, 'dvips', '0.0', 'Dvips', 'tetex-bin')
355 test_program (optional, 'fontforge', '0.0.20041224', 'FontForge',
356 'fontforge')
357 test_program (optional, 'flex', '0.0', 'Flex -- lexer generator',
358 'flex')
359 test_program (optional, 'guile', '1.6', 'GUILE scheme', 'guile')
360 test_program (optional, 'gs', '8.14',
361 'Ghostscript PostScript interpreter',
362 'gs or gs-afpl or gs-esp or gs-gpl')
363 test_program (optional, 'mftrace', '1.1.0', 'Metafont tracing Type1',
364 'mftrace')
365 test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
366 test_program (optional, 'perl', '4.0',
367 'Perl practical efficient readonly language', 'perl')
368 #test_program (optional, 'ps2pdf', '0.0', 'Ps2pdf', 'gs')
370 def CheckYYCurrentBuffer (context):
371 context.Message ('Checking for yy_current_buffer... ')
372 ret = conf.TryLink ("""using namespace std;
373 #include <FlexLexer.h>
374 class yy_flex_lexer: public yyFlexLexer
376 public:
377 yy_flex_lexer ()
379 yy_current_buffer = 0;
381 };""", '.cc')
382 context.Result (ret)
383 return ret
385 def CheckLibkpathseaSo (context):
386 saveCFLAGS = []
387 if context.env.has_key ('CFLAGS'):
388 saveCFLAGS = context.env['CFLAGS']
389 CFLAGS_shared_no_debugging = filter (lambda x: x != '-g',
390 saveCFLAGS)\
391 + ['-shared']
392 # FIXME: how does this work, with scons
393 context.env.Replace (CFLAGS = CFLAGS_shared_no_debugging)
394 #context.env.Replace (CFLAGS = '')
395 #context.env.Append (CFLAGS = ['-shared'])
396 context.Message ('Checking for libkpathsea... ')
397 ret = conf.TryLink ('''#include <kpathsea/kpathsea.h>
398 int main ()
400 kpse_var_expand ("\$TEXMF");
401 return 0;
403 ''', '.c')
404 context.env.Replace (CFLAGS = saveCFLAGS)
405 # FIXME: this prints 'ok' already
406 context.Result (ret)
407 if not ret:
408 return 0
410 sys.stdout.write ('Checking for libkpathsea.so... ')
411 testfile = str (context.sconf.lastTarget)
412 shared_size = os.path.getsize (testfile)
413 ret = shared_size < 40000
414 if ret:
415 print 'ok'
416 else:
417 print 'no'
418 return ret
420 conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
421 : CheckYYCurrentBuffer,
422 'CheckLibkpathseaSo'
423 : CheckLibkpathseaSo })
425 defines = {
426 'DIRSEP' : "'%s'" % os.sep,
427 'PATHSEP' : "'%s'" % os.pathsep,
428 'PACKAGE': '"%s"' % package.name,
429 'DATADIR' : '"%s"' % sharedir,
430 'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
431 'LOCALEDIR' : '"%s"' %localedir,
433 conf.env.Append (DEFINES = defines)
435 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
436 PYTHON_INCLUDE = os.popen (command).read ()#[:-1]
437 if env['fast']:
438 env.Append (CCFLAGS = ['-I%s' % PYTHON_INCLUDE])
439 else:
440 env.Append (CPPPATH = [PYTHON_INCLUDE])
442 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'libio.h',
443 'Python.h')
444 for i in headers:
445 if conf.CheckCHeader (i):
446 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
447 conf.env['DEFINES'][key] = 1
449 ccheaders = ('sstream',)
450 for i in ccheaders:
451 if conf.CheckCXXHeader (i):
452 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
453 conf.env['DEFINES'][key] = 1
455 functions = ('fopencookie', 'funopen',
456 'gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
457 for i in functions:
458 if 0 or conf.CheckFunc (i):
459 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
460 conf.env['DEFINES'][key] = 1
462 if conf.CheckYYCurrentBuffer ():
463 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
465 if conf.CheckLibkpathseaSo ():
466 conf.env['DEFINES']['HAVE_LIBKPATHSEA_SO'] = '1'
468 if conf.CheckLib ('dl'):
469 pass
471 if conf.CheckLib ('kpathsea'):
472 conf.env['DEFINES']['KPATHSEA'] = 1
474 # huh?
475 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
476 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
477 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
478 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
480 if env['fast']:
481 cpppath = []
482 if env.has_key ('CPPPATH'):
483 cpppath = env['CPPPATH']
485 ## FIXME: linkage, check for libguile.h and scm_boot_guile
486 #this could happen after flower...
487 env.ParseConfig ('guile-config compile')
489 test_program (required, 'pkg-config', '0.9.0',
490 'pkg-config library compile manager', 'pkg-config')
491 if test_lib (required, 'freetype2', '0.0',
492 'Development files for FreeType 2 font engine',
493 'freetype6'):
494 conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
496 if test_lib (required, 'pangoft2', '1.6.0',
497 'Development files for pango, with FreeType2',
498 'pango1.0'):
499 conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
500 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
502 if test_lib (optional, 'fontconfig', '2.2.0',
503 'Development files for fontconfig', 'fontconfig1'):
504 conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
506 #this could happen only for compiling pango-*
507 if env['gui']:
508 test_lib (required, 'gtk+-2.0', '2.4.0',
509 'Development files for GTK+', 'gtk2.0')
510 if test_lib (required, 'pango', '1.6.0',
511 'Development files for pango', 'pango1.0'):
512 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
514 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
515 conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
516 if env['fast']:
517 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
518 # <system-dir>] speeds up SCons
519 env['CCFLAGS'] += map (lambda x: '-I' + x,
520 env['CPPPATH'][len (cpppath):])
521 env['CPPPATH'] = cpppath
523 if required:
524 print
525 print '********************************'
526 print 'Please install required packages'
527 for i in required:
528 print '%s: %s-%s or newer (found: %s %s)' % i
529 Exit (1)
531 if optional:
532 print
533 print '*************************************'
534 print 'Consider installing optional packages'
535 for i in optional:
536 print '%s: %s-%s or newer (found: %s %s)' % i
538 return conf.Finish ()
540 def config_header (target, source, env):
541 config = open (str (target[0]), 'w')
542 for i in list_sort (env['DEFINES'].keys ()):
543 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
544 config.close ()
545 env.Command (config_hh, config_cache, config_header)
547 # hmm?
548 def xuniquify (lst):
549 n = []
550 for i in lst:
551 if not i in n:
552 n.append (i)
553 lst = n
554 return lst
556 def uniquify (lst):
557 d = {}
558 n = len (lst)
559 i = 0
560 while i < n:
561 if not d.has_key (lst[i]):
562 d[lst[i]] = 1
563 i += 1
564 else:
565 del lst[i]
566 n -= 1
567 return lst
569 def uniquify_config_vars (env):
570 for i in config_vars:
571 if env.has_key (i) and type (env[i]) == type ([]):
572 env[i] = uniquify (env[i])
574 def save_config_cache (env):
575 ## FIXME: Is this smart, using option cache for saving
576 ## config.cache? I cannot seem to find the official method.
577 uniquify_config_vars (env)
578 opts.Save (config_cache, env)
580 if 'config' in COMMAND_LINE_TARGETS:
581 sys.stdout.write ('\n')
582 sys.stdout.write ('LilyPond configured')
583 sys.stdout.write ('\n')
584 sys.stdout.write ('Now run')
585 sys.stdout.write ('\n')
586 sys.stdout.write (' scons [TARGET|DIR]...')
587 sys.stdout.write ('\n')
588 sys.stdout.write ('\n')
589 sys.stdout.write ('Examples:')
590 sys.stdout.write ('\n')
591 sys.stdout.write (' scons lily # build lilypond')
592 sys.stdout.write ('\n')
593 sys.stdout.write (' scons all # build everything')
594 sys.stdout.write ('\n')
595 sys.stdout.write (' scons doc # build documentation')
596 sys.stdout.write ('\n')
597 ## TODO
598 ## sys.stdout.write (' scons prefix=/usr DESTDIR=/tmp/pkg all install')
599 ## sys.stdout.write ('\n')
600 Exit (0)
601 elif not env['checksums']:
602 # When using timestams, config.hh is NEW. The next
603 # build triggers recompilation of everything. Exiting
604 # here makes SCons use the actual timestamp for config.hh
605 # and prevents recompiling everything the next run.
606 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
607 sys.stdout.write ('Running %s ... ' % command)
608 sys.stdout.write ('\n')
609 s = os.system (command)
610 Exit (s)
612 # WTF?
613 # scons: *** Calling Configure from Builders is not supported.
614 # env.Command (config_cache, None, configure)
615 if not os.path.exists (config_cache) \
616 or (os.stat ('SConstruct')[stat.ST_MTIME]
617 > os.stat (config_cache)[stat.ST_MTIME]):
618 env = configure (None, None, env)
619 save_config_cache (env)
620 elif env['checksums']:
621 # just save everything
622 save_config_cache (env)
624 #urg how does #/ subst work?
625 Export ('env')
626 SConscript ('buildscripts/builder.py')
628 env.PrependENVPath ('PATH',
629 os.path.join (env['absbuild'], env['out'], 'usr/bin'))
631 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond/', version)
633 env.Append (ENV = {
634 #'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond/', version),
635 'LILYPONDPREFIX' : LILYPONDPREFIX,
636 # ugh, can't use LILYPONDPREFIX here
637 #'TEXMF' : '{' + os.path.join (run_prefix, 'share/lilypond/', version)\
638 #+ ',' \
639 'TEXMF' : '{$LILYPONDPREFIX,'
640 + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
643 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
644 BUILD_LILYPOND = '$absbuild/lily/$out/lilypond ${__verbose}'
645 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py ${__verbose}'
648 # post-option environment-update
649 env.Append (
650 bindir = bindir,
651 sharedir = sharedir,
652 lilypond_datadir = sharedir_package,
653 localedir = localedir,
654 local_lilypond_datadir = sharedir_package_version,
655 lilypondprefix = lilypondprefix,
656 sharedir_package = sharedir_package,
657 sharedir_doc_package = sharedir_doc_package,
658 sharedir_package_version = sharedir_package_version,
660 # global build verbosity switch
661 __verbose = ' --verbose',
663 LILYPOND = BUILD_LILYPOND,
664 ABC2LY = BUILD_ABC2LY,
665 LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
666 LILYPOND_BOOK_FORMAT = 'texi-html',
667 MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
668 # should not be necessary
669 # PYTHONPATH = ['$absbuild/python/$out'],
670 TEXI2DVI_PAPERSIZE = '@afourpaper',
671 TEXI2DVI_FLAGS = [ '-t$TEXI2DVI_PAPERSIZE'],
672 DVIPS_PAPERSIZE = 'a4',
673 DVIPS_FLAGS = ['-t$DVIPS_PAPERSIZE',
674 '-u+lilypond.map',
675 '-u+ec-mftrace.map'],
676 PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
679 if env['debugging']:
680 env.Append (CCFLAGS = ['-g', '-pipe'])
681 if env['optimising']:
682 env.Append (CCFLAGS = '-O2')
683 env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
684 if env['warnings']:
685 env.Append (CCFLAGS = ['-W', '-Wall'])
686 env.Append (CXXFLAGS = ['-Wconversion'])
688 # ugr,huh?
689 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
691 if env['verbose']:
692 env['__verbose'] = ' --verbose'
693 env['set__x'] = 'set -x;'
696 ## Explicit target and dependencies
698 if 'clean' in COMMAND_LINE_TARGETS:
699 # ugh: prevent reconfigure instead of clean
700 os.system ('touch %s' % config_cache)
702 command = sys.argv[0] + ' -c .'
703 sys.stdout.write ('Running %s ... ' % command)
704 sys.stdout.write ('\n')
705 s = os.system (command)
706 if os.path.exists (config_cache):
707 os.unlink (config_cache)
708 Exit (s)
710 if 'sconsclean' in COMMAND_LINE_TARGETS:
711 command = 'rm -rf scons.cache $(find . -name ".scon*")'
712 s = os.system (command)
713 if os.path.exists (config_cache):
714 os.unlink (config_cache)
715 Exit (s)
717 if 'realclean' in COMMAND_LINE_TARGETS:
718 command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
719 sys.stdout.write ('Running %s ... ' % command)
720 sys.stdout.write ('\n')
721 s = os.system (command)
722 if os.path.exists (config_cache):
723 os.unlink (config_cache)
724 Exit (s)
726 # Declare SConscript phonies
727 env.Alias ('minimal', config_cache)
728 env.Alias ('mf-essential', config_cache)
730 env.Alias ('minimal', ['lily', 'mf-essential'])
731 env.Alias ('all', ['minimal', 'mf', '.'])
732 # Do we want the doc/web separation?
733 env.Alias ('doc',
734 ['Documentation',
735 'Documentation/user',
736 'Documentation/topdocs',
737 'Documentation/bibliography',
738 'input'])
740 # Without target arguments, do minimal build
741 if not COMMAND_LINE_TARGETS:
742 env.Default (['minimal'])
744 # GNU Make rerouting compat:
745 env.Alias ('web', 'doc')
748 env.Command (version_hh, '#/VERSION',
749 '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
751 # post-config environment update
752 env.Append (
753 run_prefix = run_prefix,
754 LILYPONDPREFIX = LILYPONDPREFIX,
756 # FIXME: move to lily/SConscript?
757 LIBPATH = [os.path.join (absbuild, 'flower', env['out']),
758 os.path.join (absbuild, 'kpath-guile', env['out']),
759 os.path.join (absbuild, 'ttftool', env['out']),],
760 CPPPATH = [outdir, ],
761 LILYPOND_PATH = ['.', '$srcdir/input',
762 '$srcdir/input/regression',
763 '$srcdir/input/test',
764 '$srcdir/input/tutorial',
765 '$srcdir/Documentation/user',
766 '$absbuild/mf/$out',
767 # os.path.join (absbuild, 'Documentation',
768 # env['out']),
769 # os.path.join (absbuild, 'Documentation/user',
770 # env['out']),
772 MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
773 '$absbuild/Documentation/user/$out'],
776 def symlink_tree (target, source, env):
777 def mkdirs (dir):
778 def mkdir (dir):
779 if not dir:
780 os.chdir (os.sep)
781 return
782 if not os.path.isdir (dir):
783 if os.path.exists (dir):
784 os.unlink (dir)
785 os.mkdir (dir)
786 os.chdir (dir)
787 map (mkdir, string.split (dir, os.sep))
788 def symlink (src, dst):
789 os.chdir (absbuild)
790 dir = os.path.dirname (dst)
791 mkdirs (dir)
792 if src[0] == '#':
793 frm = os.path.join (srcdir, src[1:])
794 else:
795 depth = len (string.split (dir, '/'))
796 if src.find ('@') > -1:
797 frm = os.path.join ('../' * depth,
798 string.replace (src, '@',
799 env['out']))
800 else:
801 frm = os.path.join ('../' * depth, src,
802 env['out'])
803 if src[-1] == '/':
804 frm = os.path.join (frm, os.path.basename (dst))
805 if env['verbose']:
806 print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
807 os.symlink (frm, os.path.basename (dst))
808 shutil.rmtree (run_prefix)
809 prefix = os.path.join (env['out'], 'usr')
810 map (lambda x: symlink (x[0], os.path.join (prefix,
811 x[1] % {'ver' : version})),
812 # ^# := source dir
813 # @ := out
814 # /$ := add dst file_name
815 (('python', 'lib/lilypond/python'),
816 # ugh
817 ('python', 'share/lilypond/%(ver)s/python'),
818 ('lily/', 'bin/lilypond'),
819 ('scripts/', 'bin/convert-ly'),
820 ('scripts/', 'bin/lilypond-book'),
821 ('scripts/', 'bin/ps2png'),
822 ('mf', 'share/lilypond/%(ver)s/dvips/mf-out'),
823 ('#ps', 'share/lilypond/%(ver)s/dvips/ps'),
824 ('#ps', 'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
825 ('mf', 'share/lilypond/%(ver)s/otf'),
826 ('mf', 'share/lilypond/%(ver)s/tfm'),
827 ('tex', 'share/lilypond/%(ver)s/tex/enc'),
828 ('#mf', 'share/lilypond/%(ver)s/fonts/mf'),
829 ('mf', 'share/lilypond/%(ver)s/fonts/map'),
830 ('mf', 'share/lilypond/%(ver)s/fonts/otf'),
831 ('mf', 'share/lilypond/%(ver)s/fonts/tfm'),
832 ('mf', 'share/lilypond/%(ver)s/fonts/type1'),
833 ('#tex', 'share/lilypond/%(ver)s/tex/source'),
834 ('tex', 'share/lilypond/%(ver)s/tex/tex-out'),
835 ('mf', 'share/lilypond/%(ver)s/tex/mf-out'),
836 ('#ly', 'share/lilypond/%(ver)s/ly'),
837 ('#scm', 'share/lilypond/%(ver)s/scm'),
838 ('#scripts', 'share/lilypond/%(ver)s/scripts'),
839 ('#ps', 'share/lilypond/%(ver)s/ps'),
840 ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
841 ('elisp', 'share/lilypond/%(ver)s/elisp')))
843 print "FIXME: BARF BARF BARF"
844 os.chdir (absbuild)
845 out = env['out']
846 ver = version
847 prefix = os.path.join (env['out'], 'usr/share/lilypond/%(ver)s/fonts'
848 % vars ())
849 for ext in ('enc', 'map', 'otf', 'svg', 'tfm', 'pfa'):
850 dir = os.path.join (absbuild, prefix, ext)
851 os.system ('rm -f ' + dir)
852 mkdirs (dir)
853 os.chdir (dir)
854 os.system ('ln -s ../../../../../../../mf/%(out)s/*.%(ext)s .'
855 % vars ())
856 os.chdir (srcdir)
858 if env['debugging']:
859 stamp = os.path.join (run_prefix, 'stamp')
860 env.Command (stamp, ['#/SConstruct', '#/VERSION'],
861 [symlink_tree, 'touch $TARGET'])
862 env.Depends ('lily', stamp)
864 #### dist, tar
865 def plus (a, b):
866 a + b
868 def cvs_entry_is_dir (line):
869 return line[0] == 'D' and line[-2] == '/'
871 def cvs_entry_is_file (line):
872 return line[0] == '/' and line[-2] == '/'
874 def cvs_dirs (dir):
875 ENTRIES = os.path.join (dir, 'CVS/Entries')
876 if not os.path.exists (ENTRIES):
877 return []
878 entries = open (ENTRIES).readlines ()
879 dir_entries = filter (cvs_entry_is_dir, entries)
880 dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
881 dir_entries)
882 return dirs + map (cvs_dirs, dirs)
884 def cvs_files (dir):
885 ENTRIES = os.path.join (dir, 'CVS/Entries')
886 entries = open (ENTRIES).readlines ()
887 file_entries = filter (cvs_entry_is_file, entries)
888 files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
889 return map (lambda x: os.path.join (dir, x), files)
891 def flatten (tree, lst):
892 if type (tree) == type ([]):
893 for i in tree:
894 if type (i) == type ([]):
895 flatten (i, lst)
896 else:
897 lst.append (i)
898 return lst
900 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
901 subdirs = flatten (cvs_dirs ('.'), [])
902 else:
903 # ugh
904 command = 'cd %(srcdir)s \
905 && find . -name SConscript | sed s@/SConscript@@' % vars ()
906 subdirs = string.split (os.popen (command).read ())
908 if env['fast']\
909 and 'all' not in COMMAND_LINE_TARGETS\
910 and 'doc' not in COMMAND_LINE_TARGETS\
911 and 'web' not in COMMAND_LINE_TARGETS\
912 and 'install' not in COMMAND_LINE_TARGETS\
913 and 'clean' not in COMMAND_LINE_TARGETS:
914 subdirs = ['lily', 'lily/include',
915 'flower', 'flower/include',
916 'kpath-guile',
917 'ttftool',
918 'mf',
921 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
922 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
923 else:
924 src_files = ['foobar']
926 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
927 txt_files = map (lambda x: x + '.txt', readme_files)
931 # speeds up build by +- 5%
933 if not env['fast']:
934 foo = map (lambda x: env.TXT (x + '.txt',
935 os.path.join ('Documentation/topdocs', x)),
936 readme_files)
937 tar_base = package.name + '-' + version
938 tar_name = tar_base + '.tar.gz'
939 ball_prefix = os.path.join (outdir, tar_base)
940 tar_ball = os.path.join (outdir, tar_name)
942 dist_files = src_files + txt_files
943 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
944 map (lambda x: env.Depends (tar_ball, x), ball_files)
945 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
946 'ln $SOURCE $TARGET'), dist_files)
947 tar = env.Command (tar_ball, src_files,
948 ['rm -f $$(find $TARGET.dir -name .sconsign)',
949 'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
950 env.Alias ('tar', tar)
952 dist_ball = os.path.join (package.release_dir, tar_name)
953 env.Command (dist_ball, tar_ball,
954 'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
955 + 'ln $SOURCE $TARGET')
956 env.Depends ('dist', dist_ball)
957 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
958 patch = env.PATCH (patch_name, tar_ball)
959 env.Depends (patch_name, dist_ball)
960 env.Alias ('release', patch)
962 #### web
963 if not env['fast']:
964 web_base = os.path.join (outdir, 'web')
965 web_ball = web_base + '.tar.gz'
966 env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
967 web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
968 web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
969 env['web_path'] = web_path
970 web_list = os.path.join (outdir, 'weblist')
971 # compatible make heritits
972 # fixme: generate in $outdir is cwd/builddir
973 env.Command (web_list,
974 ## Adding 'doc' dependency is correct, but takes
975 ## > 5min extra if you have a peder :-)
976 #'doc',
978 '#/VERSION',
979 ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
980 'cd $absbuild && $footify $$(find . -name "*.html" -print)',
981 'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
982 'cd $absbuild && find Documentation input $web_path \
983 > $TARGET',
984 '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
985 '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
986 'cd $absbuild && ls *.html >> $TARGET',])
987 env.Command (web_ball, web_list,
988 ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
989 #env.Alias ('web', web_ball)
990 www_base = os.path.join (outdir, 'www')
991 www_ball = www_base + '.tar.gz'
992 env.Command (www_ball, web_ball,
993 ['rm -rf $out/tmp',
994 'mkdir -p $absbuild/$out/tmp',
995 'tar -C $absbuild/$out/tmp -xzf $SOURCE',
996 'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
997 + ' do mv $$i $$(dirname $$i)/out-www; done',
998 'tar -C $absbuild/$out/tmp -czf $TARGET .'])
999 env.Alias ('web', www_ball)
1001 #### tags
1002 env.Append (
1003 ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
1004 --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
1005 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
1006 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
1007 src_files),
1008 'etags $ETAGSFLAGS $SOURCES')
1010 # Note: SConscripts are only needed in directories where something needs
1011 # to be done, building or installing
1012 for d in subdirs:
1013 if os.path.exists (os.path.join (d, 'SConscript')):
1014 b = os.path.join (env['build'], d, env['out'])
1015 # Support clean sourcetree build (--srcdir build)
1016 # and ./out build.
1017 if os.path.abspath (b) != os.path.abspath (d):
1018 env.BuildDir (b, d, duplicate = 0)
1019 SConscript (os.path.join (b, 'SConscript'))