*** empty log message ***
[lilypond/patrick.git] / SConstruct
blob9e7c40b1034e86f96d75b518ae791e7633fe1164
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 libdir_package = os.path.join (libdir, package.name)
252 lidbir_package_version = os.path.join (libdir_package, version)
253 localedir = os.path.join (sharedir, 'locale')
254 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
255 sharedir_package = os.path.join (sharedir, package.name)
256 sharedir_package_version = os.path.join (sharedir_package, version)
257 lilypondprefix = sharedir_package_version
259 # junkme
260 env.Append (
261 absbuild = absbuild,
262 srcdir = srcdir,
266 def list_sort (lst):
267 sorted = lst
268 sorted.sort ()
269 return sorted
272 def configure (target, source, env):
273 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9]([.0-9]*[0-9])*).*$',
274 re.DOTALL)
275 def get_version (program):
276 command = '(pkg-config --modversion %(program)s || %(program)s --version || %(program)s -V) 2>&1' % vars ()
277 pipe = os.popen (command)
278 output = pipe.read ()
279 if pipe.close ():
280 return None
281 v = re.sub (vre, '\\1', output)
282 if v[-1] == '\n':
283 v = v[:-1]
284 return string.split (v, '.')
286 def test_version (lst, full_name, minimal, description, package):
287 program = os.path.basename (full_name)
288 sys.stdout.write ('Checking %s version... ' % program)
289 actual = get_version (program)
290 if not actual:
291 print 'not found'
292 lst.append ((description, package, minimal, program,
293 'not installed'))
294 return 0
295 print string.join (actual, '.')
296 if map (string.atoi, actual) \
297 < map (string.atoi, string.split (minimal, '.')):
298 lst.append ((description, package, minimal, program,
299 string.join (actual, '.')))
300 return 0
301 return 1
303 def test_program (lst, program, minimal, description, package):
304 key = program.upper ()
305 if key.find ('+-'):
306 key = re.sub ('\+', 'X', key)
307 key = re.sub ('-', '_', key)
308 sys.stdout.write ('Checking for %s ... ' % program)
309 if env.has_key (key):
310 f = env[key]
311 sys.stdout.write ('(cached) ')
312 else:
313 f = WhereIs (program)
314 env[key] = f
315 if not f:
316 print 'not found'
317 lst.append ((description, package, minimal, program,
318 'not installed'))
319 return 0
320 print f
321 return test_version (lst, program, minimal, description, package)
323 def test_lib (lst, program, minimal, description, package):
324 # FIXME: test for Debian or RPM (or -foo?) based dists
325 # to guess (or get correct!: apt-cache search?)
326 # package name.
327 #if os.system ('pkg-config --atleast-version=0 freetype2'):
328 # barf
329 if test_version (lst, program, minimal, description,
330 'lib%(package)s-dev or %(package)s-devel'
331 % vars ()):
332 env.ParseConfig ('pkg-config --cflags --libs %(program)s'
333 % vars ())
334 return 1
335 return 0
337 required = []
338 test_program (required, 'bash', '2.0', 'Bash', 'bash')
339 test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
340 test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
341 test_program (required, 'guile-config', '1.6', 'GUILE development',
342 'libguile-dev or guile-devel')
343 test_program (required, 'mf', '0.0', 'Metafont', 'tetex-bin')
344 test_program (required, 'mftrace', '1.1.9',
345 'mftrace (http://xs4all.nl/~hanwen/mftrace)', 'mftrace')
346 test_program (required, 'potrace', '0.0', 'Potrace', 'potrace')
347 test_program (required, 'python', '2.1', 'Python (www.python.org)',
348 'python')
349 test_program (required, 'sh', '0.0', 'Bourne shell', 'sh')
351 optional = []
352 # Do not use bison 1.50 and 1.75.
353 #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
354 test_program (optional, 'bison', '1.25', 'Bison -- parser generator',
355 'bison')
356 test_program (optional, 'dvips', '0.0', 'Dvips', 'tetex-bin')
357 test_program (optional, 'fontforge', '0.0.20041224', 'FontForge',
358 'fontforge')
359 test_program (optional, 'flex', '0.0', 'Flex -- lexer generator',
360 'flex')
361 test_program (optional, 'guile', '1.6', 'GUILE scheme', 'guile')
362 test_program (optional, 'gs', '8.14',
363 'Ghostscript PostScript interpreter',
364 'gs or gs-afpl or gs-esp or gs-gpl')
365 test_program (optional, 'mftrace', '1.1.0', 'Metafont tracing Type1',
366 'mftrace')
367 test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
368 test_program (optional, 'perl', '4.0',
369 'Perl practical efficient readonly language', 'perl')
370 #test_program (optional, 'ps2pdf', '0.0', 'Ps2pdf', 'gs')
372 def CheckYYCurrentBuffer (context):
373 context.Message ('Checking for yy_current_buffer... ')
374 ret = conf.TryCompile ("""using namespace std;
375 #include <FlexLexer.h>
376 class yy_flex_lexer: public yyFlexLexer
378 public:
379 yy_flex_lexer ()
381 yy_current_buffer = 0;
383 };""", '.cc')
384 context.Result (ret)
385 return ret
387 def CheckLibkpathseaSo (context):
388 saveCFLAGS = []
389 if context.env.has_key ('CFLAGS'):
390 saveCFLAGS = context.env['CFLAGS']
391 CFLAGS_shared_no_debugging = filter (lambda x: x != '-g',
392 saveCFLAGS)\
393 + ['-shared']
394 # FIXME: how does this work, with scons
395 context.env.Replace (CFLAGS = CFLAGS_shared_no_debugging)
396 #context.env.Replace (CFLAGS = '')
397 #context.env.Append (CFLAGS = ['-shared'])
398 context.Message ('Checking for libkpathsea... ')
399 ret = conf.TryLink ('''#include <kpathsea/kpathsea.h>
400 int main ()
402 kpse_var_expand ("\$TEXMF");
403 return 0;
405 ''', '.c')
406 context.env.Replace (CFLAGS = saveCFLAGS)
407 # FIXME: this prints 'ok' already
408 context.Result (ret)
409 if not ret:
410 return 0
412 sys.stdout.write ('Checking for libkpathsea.so... ')
413 testfile = str (context.sconf.lastTarget)
414 shared_size = os.path.getsize (testfile)
415 ret = shared_size < 40000
416 if ret:
417 print 'ok'
418 else:
419 print 'no'
420 return ret
422 conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
423 : CheckYYCurrentBuffer,
424 'CheckLibkpathseaSo'
425 : CheckLibkpathseaSo })
427 defines = {
428 'DIRSEP' : "'%s'" % os.sep,
429 'PATHSEP' : "'%s'" % os.pathsep,
430 'PACKAGE': '"%s"' % package.name,
431 'DATADIR' : '"%s"' % sharedir,
432 'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
433 'LOCALEDIR' : '"%s"' %localedir,
435 conf.env.Append (DEFINES = defines)
437 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
438 PYTHON_INCLUDE = os.popen (command).read ()#[:-1]
439 if env['fast']:
440 env.Append (CCFLAGS = ['-I%s' % PYTHON_INCLUDE])
441 else:
442 env.Append (CPPPATH = [PYTHON_INCLUDE])
444 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'libio.h',
445 'Python.h')
446 for i in headers:
447 if conf.CheckCHeader (i):
448 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
449 conf.env['DEFINES'][key] = 1
451 ccheaders = ('sstream',)
452 for i in ccheaders:
453 if conf.CheckCXXHeader (i):
454 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
455 conf.env['DEFINES'][key] = 1
457 functions = ('fopencookie', 'funopen',
458 'gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
459 for i in functions:
460 if 0 or conf.CheckFunc (i):
461 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
462 conf.env['DEFINES'][key] = 1
464 if conf.CheckYYCurrentBuffer ():
465 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
467 if conf.CheckLibkpathseaSo ():
468 conf.env['DEFINES']['HAVE_LIBKPATHSEA_SO'] = '1'
470 if conf.CheckLib ('dl'):
471 pass
473 if conf.CheckLib ('kpathsea'):
474 conf.env['DEFINES']['KPATHSEA'] = 1
476 # huh?
477 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
478 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
479 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
480 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
482 if env['fast']:
483 cpppath = []
484 if env.has_key ('CPPPATH'):
485 cpppath = env['CPPPATH']
487 ## FIXME: linkage, check for libguile.h and scm_boot_guile
488 #this could happen after flower...
489 env.ParseConfig ('guile-config compile')
491 test_program (required, 'pkg-config', '0.9.0',
492 'pkg-config library compile manager', 'pkg-config')
493 if test_lib (required, 'freetype2', '0.0',
494 'Development files for FreeType 2 font engine',
495 'freetype6'):
496 conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
498 if test_lib (required, 'pangoft2', '1.6.0',
499 'Development files for pango, with FreeType2',
500 'pango1.0'):
501 conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
502 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
504 if test_lib (optional, 'fontconfig', '2.2.0',
505 'Development files for fontconfig', 'fontconfig1'):
506 conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
508 #this could happen only for compiling pango-*
509 if env['gui']:
510 test_lib (required, 'gtk+-2.0', '2.4.0',
511 'Development files for GTK+', 'gtk2.0')
512 if test_lib (required, 'pango', '1.6.0',
513 'Development files for pango', 'pango1.0'):
514 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
516 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
517 conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
518 if env['fast']:
519 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
520 # <system-dir>] speeds up SCons
521 env['CCFLAGS'] += map (lambda x: '-I' + x,
522 env['CPPPATH'][len (cpppath):])
523 env['CPPPATH'] = cpppath
525 if required:
526 print
527 print '********************************'
528 print 'Please install required packages'
529 for i in required:
530 print '%s: %s-%s or newer (found: %s %s)' % i
531 Exit (1)
533 if optional:
534 print
535 print '*************************************'
536 print 'Consider installing optional packages'
537 for i in optional:
538 print '%s: %s-%s or newer (found: %s %s)' % i
540 return conf.Finish ()
542 def config_header (target, source, env):
543 config = open (str (target[0]), 'w')
544 for i in list_sort (env['DEFINES'].keys ()):
545 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
546 config.close ()
547 env.Command (config_hh, config_cache, config_header)
549 # hmm?
550 def xuniquify (lst):
551 n = []
552 for i in lst:
553 if not i in n:
554 n.append (i)
555 lst = n
556 return lst
558 def uniquify (lst):
559 d = {}
560 n = len (lst)
561 i = 0
562 while i < n:
563 if not d.has_key (lst[i]):
564 d[lst[i]] = 1
565 i += 1
566 else:
567 del lst[i]
568 n -= 1
569 return lst
571 def uniquify_config_vars (env):
572 for i in config_vars:
573 if env.has_key (i) and type (env[i]) == type ([]):
574 env[i] = uniquify (env[i])
576 def save_config_cache (env):
577 ## FIXME: Is this smart, using option cache for saving
578 ## config.cache? I cannot seem to find the official method.
579 uniquify_config_vars (env)
580 opts.Save (config_cache, env)
582 if 'config' in COMMAND_LINE_TARGETS:
583 sys.stdout.write ('\n')
584 sys.stdout.write ('LilyPond configured')
585 sys.stdout.write ('\n')
586 sys.stdout.write ('Now run')
587 sys.stdout.write ('\n')
588 sys.stdout.write (' scons [TARGET|DIR]...')
589 sys.stdout.write ('\n')
590 sys.stdout.write ('\n')
591 sys.stdout.write ('Examples:')
592 sys.stdout.write ('\n')
593 sys.stdout.write (' scons lily # build lilypond')
594 sys.stdout.write ('\n')
595 sys.stdout.write (' scons all # build everything')
596 sys.stdout.write ('\n')
597 sys.stdout.write (' scons doc # build documentation')
598 sys.stdout.write ('\n')
599 ## TODO
600 ## sys.stdout.write (' scons prefix=/usr DESTDIR=/tmp/pkg all install')
601 ## sys.stdout.write ('\n')
602 Exit (0)
603 elif not env['checksums']:
604 # When using timestams, config.hh is NEW. The next
605 # build triggers recompilation of everything. Exiting
606 # here makes SCons use the actual timestamp for config.hh
607 # and prevents recompiling everything the next run.
608 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
609 sys.stdout.write ('Running %s ... ' % command)
610 sys.stdout.write ('\n')
611 s = os.system (command)
612 Exit (s)
614 # WTF?
615 # scons: *** Calling Configure from Builders is not supported.
616 # env.Command (config_cache, None, configure)
617 if not os.path.exists (config_cache) \
618 or (os.stat ('SConstruct')[stat.ST_MTIME]
619 > os.stat (config_cache)[stat.ST_MTIME]):
620 env = configure (None, None, env)
621 save_config_cache (env)
622 elif env['checksums']:
623 # just save everything
624 save_config_cache (env)
626 #urg how does #/ subst work?
627 Export ('env')
628 SConscript ('buildscripts/builder.py')
630 env.PrependENVPath ('PATH',
631 os.path.join (env['absbuild'], env['out'], 'usr/bin'))
633 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond/', version)
635 env.Append (ENV = {
636 #'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond/', version),
637 'LILYPONDPREFIX' : LILYPONDPREFIX,
638 # ugh, can't use LILYPONDPREFIX here
639 #'TEXMF' : '{' + os.path.join (run_prefix, 'share/lilypond/', version)\
640 #+ ',' \
641 'TEXMF' : '{$LILYPONDPREFIX,'
642 + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
645 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
646 BUILD_LILYPOND = '$absbuild/lily/$out/lilypond ${__verbose}'
647 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py ${__verbose}'
650 # post-option environment-update
651 env.Append (
652 bindir = bindir,
653 sharedir = sharedir,
654 lilypond_datadir = sharedir_package,
655 localedir = localedir,
656 local_lilypond_datadir = sharedir_package_version,
657 lilypondprefix = lilypondprefix,
658 sharedir_package = sharedir_package,
659 sharedir_doc_package = sharedir_doc_package,
660 sharedir_package_version = sharedir_package_version,
662 # global build verbosity switch
663 __verbose = ' --verbose',
665 LILYPOND = BUILD_LILYPOND,
666 ABC2LY = BUILD_ABC2LY,
667 LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
668 LILYPOND_BOOK_FORMAT = 'texi-html',
669 MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
670 # should not be necessary
671 # PYTHONPATH = ['$absbuild/python/$out'],
672 TEXI2DVI_PAPERSIZE = '@afourpaper',
673 TEXI2DVI_FLAGS = [ '-t$TEXI2DVI_PAPERSIZE'],
674 DVIPS_PAPERSIZE = 'a4',
675 DVIPS_FLAGS = ['-t$DVIPS_PAPERSIZE',
676 '-u+lilypond.map',
677 '-u+ec-mftrace.map'],
678 PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
681 env.Append (CCFLAGS = ['-pipe', '-Wno-pmf-conversions'])
682 if env['debugging']:
683 env.Append (CCFLAGS = ['-g'])
684 if env['optimising']:
685 env.Append (CCFLAGS = '-O2')
686 env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
687 if env['warnings']:
688 env.Append (CCFLAGS = ['-W', '-Wall'])
689 env.Append (CXXFLAGS = ['-Wconversion'])
691 # ugr,huh?
692 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
693 # FIXME: ParseConfig ignores -L flag?
694 env.Append (LINKFLAGS = ['-L/usr/X11R6/lib'])
696 if env['verbose']:
697 env['__verbose'] = ' --verbose'
698 env['set__x'] = 'set -x;'
701 ## Explicit target and dependencies
703 if 'clean' in COMMAND_LINE_TARGETS:
704 # ugh: prevent reconfigure instead of clean
705 os.system ('touch %s' % config_cache)
707 command = sys.argv[0] + ' -c .'
708 sys.stdout.write ('Running %s ... ' % command)
709 sys.stdout.write ('\n')
710 s = os.system (command)
711 if os.path.exists (config_cache):
712 os.unlink (config_cache)
713 Exit (s)
715 if 'sconsclean' in COMMAND_LINE_TARGETS:
716 command = 'rm -rf scons.cache $(find . -name ".scon*")'
717 s = os.system (command)
718 if os.path.exists (config_cache):
719 os.unlink (config_cache)
720 Exit (s)
722 if 'realclean' in COMMAND_LINE_TARGETS:
723 command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
724 sys.stdout.write ('Running %s ... ' % command)
725 sys.stdout.write ('\n')
726 s = os.system (command)
727 if os.path.exists (config_cache):
728 os.unlink (config_cache)
729 Exit (s)
731 # Declare SConscript phonies
732 env.Alias ('minimal', config_cache)
733 env.Alias ('mf-essential', config_cache)
735 env.Alias ('minimal', ['lily', 'mf-essential'])
736 env.Alias ('all', ['minimal', 'mf', '.'])
737 # Do we want the doc/web separation?
738 env.Alias ('doc',
739 ['Documentation',
740 'Documentation/user',
741 'Documentation/topdocs',
742 'Documentation/bibliography',
743 'input'])
745 # Without target arguments, do minimal build
746 if not COMMAND_LINE_TARGETS:
747 env.Default (['minimal'])
749 # GNU Make rerouting compat:
750 env.Alias ('web', 'doc')
753 env.Command (version_hh, '#/VERSION',
754 '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
756 # post-config environment update
757 env.Append (
758 run_prefix = run_prefix,
759 LILYPONDPREFIX = LILYPONDPREFIX,
761 # FIXME: move to lily/SConscript?
762 LIBPATH = [os.path.join (absbuild, 'flower', env['out']),
763 os.path.join (absbuild, 'kpath-guile', env['out']),],
764 CPPPATH = [outdir, ],
765 LILYPOND_PATH = ['.',
766 '$srcdir/input',
767 '$srcdir/input/regression',
768 '$srcdir/input/test',
769 '$srcdir/input/tutorial',
770 '$srcdir/Documentation/user',
771 '$absbuild/mf/$out',
772 # os.path.join (absbuild, 'Documentation',
773 # env['out']),
774 # os.path.join (absbuild, 'Documentation/user',
775 # env['out']),
777 MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
778 '$absbuild/Documentation/user/$out'],
781 def symlink_tree (target, source, env):
782 def mkdirs (dir):
783 def mkdir (dir):
784 if not dir:
785 os.chdir (os.sep)
786 return
787 if not os.path.isdir (dir):
788 if os.path.exists (dir):
789 os.unlink (dir)
790 os.mkdir (dir)
791 os.chdir (dir)
792 map (mkdir, string.split (dir, os.sep))
793 def symlink (src, dst):
794 os.chdir (absbuild)
795 dir = os.path.dirname (dst)
796 mkdirs (dir)
797 if src[0] == '#':
798 frm = os.path.join (srcdir, src[1:])
799 else:
800 depth = len (string.split (dir, '/'))
801 if src.find ('@') > -1:
802 frm = os.path.join ('../' * depth,
803 string.replace (src, '@',
804 env['out']))
805 else:
806 frm = os.path.join ('../' * depth, src,
807 env['out'])
808 if src[-1] == '/':
809 frm = os.path.join (frm, os.path.basename (dst))
810 if env['verbose']:
811 print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
812 os.symlink (frm, os.path.basename (dst))
813 shutil.rmtree (run_prefix)
814 prefix = os.path.join (env['out'], 'usr')
815 map (lambda x: symlink (x[0], os.path.join (prefix,
816 x[1] % {'ver' : version})),
817 # ^# := source dir
818 # @ := out
819 # /$ := add dst file_name
820 (('python', 'lib/lilypond/python'),
821 # ugh
822 ('python', 'share/lilypond/%(ver)s/python'),
823 ('lily/', 'bin/lilypond'),
824 ('scripts/', 'bin/convert-ly'),
825 ('scripts/', 'bin/lilypond-book'),
826 ('scripts/', 'bin/ps2png'),
827 ('mf', 'share/lilypond/%(ver)s/dvips/mf-out'),
828 ('#ps', 'share/lilypond/%(ver)s/dvips/ps'),
829 ('#ps', 'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
830 ('mf', 'share/lilypond/%(ver)s/otf'),
831 ('mf', 'share/lilypond/%(ver)s/tfm'),
832 ('tex', 'share/lilypond/%(ver)s/tex/enc'),
833 ('#mf', 'share/lilypond/%(ver)s/fonts/mf'),
834 ('mf', 'share/lilypond/%(ver)s/fonts/map'),
835 ('mf', 'share/lilypond/%(ver)s/fonts/otf'),
836 ('mf', 'share/lilypond/%(ver)s/fonts/tfm'),
837 ('mf', 'share/lilypond/%(ver)s/fonts/type1'),
838 ('#tex', 'share/lilypond/%(ver)s/tex/source'),
839 ('tex', 'share/lilypond/%(ver)s/tex/tex-out'),
840 ('mf', 'share/lilypond/%(ver)s/tex/mf-out'),
841 ('#ly', 'share/lilypond/%(ver)s/ly'),
842 ('#scm', 'share/lilypond/%(ver)s/scm'),
843 ('#scripts', 'share/lilypond/%(ver)s/scripts'),
844 ('#ps', 'share/lilypond/%(ver)s/ps'),
845 ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
846 ('elisp', 'share/lilypond/%(ver)s/elisp')))
848 print "FIXME: BARF BARF BARF"
849 os.chdir (absbuild)
850 out = env['out']
851 ver = version
852 prefix = os.path.join (env['out'], 'usr/share/lilypond/%(ver)s/fonts'
853 % vars ())
854 for ext in ('enc', 'map', 'otf', 'svg', 'tfm', 'pfa'):
855 dir = os.path.join (absbuild, prefix, ext)
856 os.system ('rm -f ' + dir)
857 mkdirs (dir)
858 os.chdir (dir)
859 os.system ('ln -s ../../../../../../../mf/%(out)s/*.%(ext)s .'
860 % vars ())
861 os.chdir (srcdir)
863 if 1: #env['debugging']:
864 stamp = os.path.join (run_prefix, 'stamp')
865 env.Command (stamp, ['#/SConstruct', '#/VERSION'],
866 [symlink_tree, 'touch $TARGET'])
867 env.Depends ('lily', stamp)
869 #### dist, tar
870 def plus (a, b):
871 a + b
873 def cvs_entry_is_dir (line):
874 return line[0] == 'D' and line[-2] == '/'
876 def cvs_entry_is_file (line):
877 return line[0] == '/' and line[-2] == '/'
879 def cvs_dirs (dir):
880 entries = os.path.join (dir, 'CVS/Entries')
881 if not os.path.exists (entries):
882 return []
883 entries = open (entries).readlines ()
884 dir_entries = filter (cvs_entry_is_dir, entries)
885 dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
886 dir_entries)
887 return dirs + map (cvs_dirs, dirs)
889 def cvs_files (dir):
890 entries = os.path.join (dir, 'CVS/Entries')
891 if not os.path.exists (entries):
892 return []
893 entries = open (entries).readlines ()
894 file_entries = filter (cvs_entry_is_file, entries)
895 files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
896 return map (lambda x: os.path.join (dir, x), files)
898 def flatten (tree, lst):
899 if type (tree) == type ([]):
900 for i in tree:
901 if type (i) == type ([]):
902 flatten (i, lst)
903 else:
904 lst.append (i)
905 return lst
907 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
908 subdirs = flatten (cvs_dirs ('.'), [])
909 else:
910 # ugh
911 command = 'cd %(srcdir)s \
912 && find . -name SConscript | sed s@/SConscript@@' % vars ()
913 subdirs = string.split (os.popen (command).read ())
915 if env['fast']\
916 and 'all' not in COMMAND_LINE_TARGETS\
917 and 'doc' not in COMMAND_LINE_TARGETS\
918 and 'web' not in COMMAND_LINE_TARGETS\
919 and 'install' not in COMMAND_LINE_TARGETS\
920 and 'clean' not in COMMAND_LINE_TARGETS:
921 subdirs = ['lily',
922 'flower',
923 'kpath-guile',
924 'mf',
925 'python',
928 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
929 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
930 else:
931 src_files = ['foobar']
933 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
934 txt_files = map (lambda x: x + '.txt', readme_files)
938 # speeds up build by +- 5%
940 if not env['fast']:
941 foo = map (lambda x: env.TXT (x + '.txt',
942 os.path.join ('Documentation/topdocs', x)),
943 readme_files)
944 tar_base = package.name + '-' + version
945 tar_name = tar_base + '.tar.gz'
946 ball_prefix = os.path.join (outdir, tar_base)
947 tar_ball = os.path.join (outdir, tar_name)
949 dist_files = src_files + txt_files
950 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
951 map (lambda x: env.Depends (tar_ball, x), ball_files)
952 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
953 'ln $SOURCE $TARGET'), dist_files)
954 tar = env.Command (tar_ball, src_files,
955 ['rm -f $$(find $TARGET.dir -name .sconsign)',
956 'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
957 env.Alias ('tar', tar)
959 dist_ball = os.path.join (package.release_dir, tar_name)
960 env.Command (dist_ball, tar_ball,
961 'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
962 + 'ln $SOURCE $TARGET')
963 env.Depends ('dist', dist_ball)
964 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
965 patch = env.PATCH (patch_name, tar_ball)
966 env.Depends (patch_name, dist_ball)
967 env.Alias ('release', patch)
969 #### web
970 if not env['fast']:
971 web_base = os.path.join (outdir, 'web')
972 web_ball = web_base + '.tar.gz'
973 env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
974 web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
975 web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
976 env['web_path'] = web_path
977 web_list = os.path.join (outdir, 'weblist')
978 # compatible make heritits
979 # fixme: generate in $outdir is cwd/builddir
980 env.Command (web_list,
981 ## Adding 'doc' dependency is correct, but takes
982 ## > 5min extra if you have a peder :-)
983 #'doc',
985 '#/VERSION',
986 ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
987 'cd $absbuild && $footify $$(find . -name "*.html" -print)',
988 'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
989 'cd $absbuild && find Documentation input $web_path \
990 > $TARGET',
991 '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
992 '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
993 'cd $absbuild && ls *.html >> $TARGET',])
994 env.Command (web_ball, web_list,
995 ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
996 #env.Alias ('web', web_ball)
997 www_base = os.path.join (outdir, 'www')
998 www_ball = www_base + '.tar.gz'
999 env.Command (www_ball, web_ball,
1000 ['rm -rf $out/tmp',
1001 'mkdir -p $absbuild/$out/tmp',
1002 'tar -C $absbuild/$out/tmp -xzf $SOURCE',
1003 'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
1004 + ' do mv $$i $$(dirname $$i)/out-www; done',
1005 'tar -C $absbuild/$out/tmp -czf $TARGET .'])
1006 env.Alias ('web', www_ball)
1008 #### tags
1009 env.Append (
1010 ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
1011 --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
1012 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
1013 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
1014 src_files),
1015 'etags $ETAGSFLAGS $SOURCES')
1017 # Note: SConscripts are only needed in directories where something needs
1018 # to be done, building or installing
1019 for d in subdirs:
1020 if os.path.exists (os.path.join (d, 'SConscript')):
1021 b = os.path.join (env['build'], d, env['out'])
1022 # Support clean sourcetree build (--srcdir build)
1023 # and ./out build.
1024 if os.path.abspath (b) != os.path.abspath (d):
1025 env.BuildDir (b, d, duplicate = 0)
1026 SConscript (os.path.join (b, 'SConscript'))