]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
(test_program): Bugfix for double digit version compares, use
[lilypond.git] / SConstruct
1 # -*-python-*-
2
3 '''
4 Experimental scons (www.scons.org) building.
5
6 Usage
7
8     scons TARGET
9
10 build from source directory ./TARGET (not recursive)
11
12 Configure, build
13
14     scons [config]             # configure
15     scons                      # build all
16
17 Run from build tree
18
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
23
24     #optionally, if you do not use custom.py below
25     #export LILYPONDPREFIX=$run/share/lilypond
26
27     lilypond-bin input/simple
28
29 Other targets
30     scons mf-essential         # build minimal mf stuff
31
32     scons doc                  # build web doc
33     scons config               # reconfigure
34     scons install              # install
35     scons -c                   # clean
36     scons -h                   # help
37
38     scons /                    # build *everything* (including installation)
39
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
43
44 Debugging
45     scons --debug=dtree
46     scons --debug=explain
47     scons verbose=1
48
49 Optional custom.py
50
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')
58
59 '''
60
61
62 # TODO:
63 #  * usability
64
65 #  * more program configure tests (mfont, ...?)
66
67 #  * install doc
68
69 #  * split doc target: doc input examples mutopia?
70
71 #  * grep FIXME $(find . -name 'S*t')
72
73
74 import re
75 import glob
76 import os
77 import string
78 import sys
79 import stat
80 import shutil
81
82 # duh, we need 0.95.1
83 EnsureSConsVersion (0, 95)
84
85 usage = r'''Usage:
86 scons [KEY=VALUE].. [TARGET|DIR]..
87
88 TARGETS: clean, config, doc, dist, install, mf-essential, po-update,
89          realclean, release, sconsclean, tar, TAGS
90
91 '''
92       
93
94 config_cache = 'scons.cache'
95
96 config_vars = [
97         'BASH',
98         'CCFLAGS',
99         'CPPPATH',
100         'CPPDEFINES',
101         'CXXFLAGS',
102         'DEFINES',
103         'LIBS',
104         'LINKFLAGS',
105         'METAFONT',
106         'PERL',
107         'PYTHON',
108         ]
109
110 # Put your favourite stuff in custom.py
111 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
112 opts.Add ('prefix', 'Install prefix', '/usr/')
113 opts.Add ('out', 'Output directory', 'out-scons')
114 opts.Add ('build', 'Build directory', '.')
115 opts.Add ('DESTDIR', 'DESTDIR prepended to prefix', '')
116 opts.AddOptions (
117         BoolOption ('warnings', 'compile with -Wall and similiar',
118                    1),
119         BoolOption ('debugging', 'compile with debugging symbols',
120                     0),
121         BoolOption ('optimising', 'compile with optimising',
122                     1),
123         BoolOption ('shared', 'build shared libraries',
124                     0),
125         BoolOption ('static', 'build static libraries',
126                     1),
127         BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
128                     1),
129         BoolOption ('verbose', 'run commands with verbose flag',
130                     0),
131         BoolOption ('checksums', 'use checksums instead of timestamps',
132                     0),
133         BoolOption ('fast', 'use timestamps, implicit cache, prune CPPPATH',
134                     1),
135         )
136
137 srcdir = Dir ('.').srcnode ().abspath
138 #ugh
139 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
140 import packagepython
141 package = packagepython.Package (srcdir)
142 version = packagepython.version_tuple_to_str (package.version)
143
144 ENV = { 'PATH' : os.environ['PATH'] }
145 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH']:
146         if os.environ.has_key (key):
147                 ENV[key] = os.environ[key]
148
149 env = Environment (
150         ENV = ENV,
151
152         BASH = '/bin/bash',
153         CPPDEFINES = '-DHAVE_CONFIG_H',
154         PERL = '/usr/bin/perl',
155         PYTHON = '/usr/bin/python',
156         SH = '/bin/sh',
157
158         MAKEINFO = 'LANG= makeinfo',
159         MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
160         
161         PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
162                                          'usr/pkg/gnome/lib'),
163                            os.path.join (os.environ['HOME'],
164                                          'usr/pkg/pango/lib')],
165         GZIP='-9v',
166         MFMODE = 'ljfour',
167         TOPLEVEL_VERSION = version,
168         )
169
170 # Add all config_vars to opts, so that they will be read and saved
171 # together with the other configure options.
172 map (lambda x: opts.AddOptions ((x,)), config_vars)
173
174 Help (usage + opts.GenerateHelpText (env))
175
176 opts.Update (env)
177
178 if env['fast']:
179         # Usability switch (Anthony Roach).
180         # See http://www.scons.org/cgi-bin/wiki/GoFastButton
181         # First do: scons realclean .
182         env['checksums'] = 0
183         SetOption ('max_drift', 1)
184         SetOption ('implicit_cache', 1)
185 elif env['checksums']:
186         # Always use checksums (makes more sense than timestamps).
187         SetOption ('max_drift', 0)
188         # Using *content* checksums prevents rebuilds after
189         # [re]configure if config.hh has not changed.  Too bad that it
190         # is unusably slow.
191         TargetSignatures ('content')
192
193 absbuild = Dir (env['build']).abspath
194 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
195 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
196
197
198 config_hh = os.path.join (outdir, 'config.hh')
199 version_hh = os.path.join (outdir, 'version.hh')
200
201 env.Alias ('config', config_cache)
202
203 cachedir = os.path.join (outdir, 'build-cache')
204
205 if not os.path.exists (cachedir):
206         os.makedirs (cachedir)
207
208 CacheDir (cachedir)
209
210 # No need to set $LILYPONDPREFIX to run lily, but cannot install...
211 if env['debugging'] and not 'install' in COMMAND_LINE_TARGETS:
212         env['prefix'] = run_prefix
213
214 prefix = env['prefix']
215 bindir = os.path.join (prefix, 'bin')
216 sharedir = os.path.join (prefix, 'share')
217 libdir = os.path.join (prefix, 'lib')
218 localedir = os.path.join (sharedir, 'locale')
219 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
220 sharedir_package = os.path.join (sharedir, package.name)
221 sharedir_package_version = os.path.join (sharedir_package, version)
222 lilypondprefix = sharedir_package_version
223
224 # junkme
225 env.Append (
226         absbuild = absbuild,
227         srcdir = srcdir,
228         )
229
230
231 def list_sort (lst):
232         sorted = lst
233         sorted.sort ()
234         return sorted
235
236
237 def configure (target, source, env):
238         vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$',
239                           re.DOTALL)
240         def get_version (program):
241                 command = '(pkg-config --modversion %(program)s || %(program)s --version || %(program)s -V) 2>&1' % vars ()
242                 pipe = os.popen (command)
243                 output = pipe.read ()
244                 if pipe.close ():
245                         return None
246                 v = re.sub (vre, '\\1', output)
247                 if v[-1] == '\n':
248                         v = v[:-1]
249                 return string.split (v, '.')
250
251         def test_program (lst, program, minimal, description, package):
252                 sys.stdout.write ('Checking %s version... ' % program)
253                 actual = get_version (program)
254                 if not actual:
255                         print 'not found'
256                         lst.append ((description, package, minimal, program,
257                                      'not installed'))
258                         return 0
259                 sys.stdout.write (string.join (actual, '.'))
260                 sys.stdout.write ('\n')
261                 if map (string.atoi, actual) \
262                    < map (string.atoi, string.split (minimal, '.')):
263                         lst.append ((description, package, minimal, program,
264                                      string.join (actual, '.')))
265                         return 0
266                 return 1
267
268         def test_lib (lst, program, minimal, description):
269                 # FIXME: test for Debian or RPM (or -foo?) based dists
270                 # to guess (or get correct!: apt-cache search?)
271                 # package name.
272                 #if os.system ('pkg-config --atleast-version=0 freetype2'):
273                 # barf
274                 if test_program (lst, program, minimal, description,
275                                  'lib%(program)s-dev or %(program)s-devel'
276                                  % vars ()):
277                         env.ParseConfig ('pkg-config --cflags --libs %(program)s'
278                                          % vars ())
279                         return 1
280                 return 0
281
282         for i in ['bash', 'perl', 'python', 'sh']:
283                 sys.stdout.write ('Checking for %s... ' % i)
284                 c = WhereIs (i)
285                 key = string.upper (i)
286                 if c:
287                         env[key] = c
288                         sys.stdout.write (c)
289                 else:
290                         sys.stdout.write ('not found: %s (using: %s)' \
291                                           % (c, env[key]))
292                         # Hmm? abort?
293                 sys.stdout.write ('\n')
294
295         required = []
296         test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
297         test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
298         test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
299         test_program (required, 'guile-config', '1.6', 'GUILE development',
300                         'libguile-dev or guile-devel')
301         # Do not use bison 1.50 and 1.75.
302         test_program (required, 'bison', '1.25', 'Bison -- parser generator',
303                         'bison')
304         test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
305
306         optional = []
307         test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
308         test_program (optional, 'guile', '1.6', 'GUILE scheme', 'guile')
309         test_program (optional, 'mftrace', '1.1.0', 'Metafont tracing Type1',
310                         'mftrace')
311         test_program (optional, 'perl', '4.0',
312                         'Perl practical efficient readonly language', 'perl')
313         #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
314
315         def CheckYYCurrentBuffer (context):
316                 context.Message ('Checking for yy_current_buffer... ')
317                 ret = conf.TryCompile ("""using namespace std;
318                 #include <FlexLexer.h>
319                 class yy_flex_lexer: public yyFlexLexer
320                 {
321                 public:
322                 yy_flex_lexer ()
323                 {
324                 yy_current_buffer = 0;
325                 }
326                 };""", '.cc')
327                 context.Result (ret)
328                 return ret
329
330         conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
331                                                 : CheckYYCurrentBuffer })
332
333         defines = {
334            'DIRSEP' : "'%s'" % os.sep,
335            'PATHSEP' : "'%s'" % os.pathsep,
336            'PACKAGE': '"%s"' % package.name,
337            'DATADIR' : '"%s"' % sharedir,
338            'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
339            'LOCALEDIR' : '"%s"' %localedir,
340         }
341         conf.env.Append (DEFINES = defines)
342
343         command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
344         PYTHON_INCLUDE = os.popen (command).read ()
345         if env['fast']:
346                 env.Append (CCFLAGS = ['-I%s ' % PYTHON_INCLUDE])
347         else:
348                 env.Append (CPPPATH = PYTHON_INCLUDE)
349
350         headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'libio.h',
351                    'Python.h')
352         for i in headers:
353                 if conf.CheckCHeader (i):
354                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
355                         conf.env['DEFINES'][key] = 1
356
357         ccheaders = ('sstream',)
358         for i in ccheaders:
359                 if conf.CheckCXXHeader (i):
360                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
361                         conf.env['DEFINES'][key] = 1
362
363         functions = ('fopencookie', 'funopen',
364                      'gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
365         for i in functions:
366                 if 0 or conf.CheckFunc (i):
367                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
368                         conf.env['DEFINES'][key] = 1
369
370         if conf.CheckYYCurrentBuffer ():
371                 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
372
373         if conf.CheckLib ('dl'):
374                 pass
375
376         if conf.CheckLib ('kpathsea'):
377                 conf.env['DEFINES']['KPATHSEA'] = 1
378
379         # huh? 
380         if conf.CheckLib ('kpathsea', 'kpse_find_file'):
381                 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
382         if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
383                 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
384
385         # FIXME fc3 - move to kpath-guile/SConscript?
386         conf.env['DEFINES']['HAVE_LIBKPATHSEA_SO'] = '1'
387
388         if env['fast']:
389                 cpppath = []
390                 if env.has_key ('CPPPATH'):
391                         cpppath = env['CPPPATH']
392
393         ## FIXME: linkage, check for libguile.h and scm_boot_guile
394         #this could happen after flower...
395         env.ParseConfig ('guile-config compile')
396
397         test_program (required, 'pkg-config', '0.9.0',
398                       'pkg-config library compile manager', 'pkg-config')
399         if test_lib (required, 'freetype2', '0.0',
400                      'Development files for FreeType 2 font engine'):
401                 conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
402                 
403         if test_lib (required, 'pangoft2', '1.6.0',
404                      'Development files for pango, with fontconfig2'):
405                 conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
406                 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
407
408         if test_lib (optional, 'fontconfig', '2.2.0',
409                      'Development files for fontconfig'):
410                 conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
411         
412         #this could happen only for compiling pango-*
413         if env['gui']:
414                 test_lib (required, 'gtk+-2.0', '2.4.0',
415                           'Development files for GTK+')
416                 if test_lib (required, 'pango', '1.6.0',
417                           'Development files for pango'):
418                         conf.env['DEFINES']['HAVE_PANGO16'] = '1'
419                         
420                 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
421                         conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
422                 if conf.CheckLib ('pango-1.0',
423                                   'pango_fc_font_map_add_decoder_find_func'):
424                         conf.env['DEFINES']['HAVE_PANGO16'] = '1'
425                         conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
426
427         if env['fast']:
428                 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
429                 # <system-dir>] speeds up SCons
430                 env['CCFLAGS'] += map (lambda x: '-I' + x,
431                                        env['CPPPATH'][len (cpppath):])
432                 env['CPPPATH'] = cpppath
433
434         if required:
435                 print
436                 print '********************************'
437                 print 'Please install required packages'
438                 for i in required:
439                         print '%s:      %s-%s or newer (found: %s %s)' % i
440                 Exit (1)
441
442         if optional:
443                 print
444                 print '*************************************'
445                 print 'Consider installing optional packages'
446                 for i in optional:
447                         print '%s:      %s-%s or newer (found: %s %s)' % i
448
449         return conf.Finish ()
450
451 def config_header (target, source, env):
452         config = open (str (target[0]), 'w')
453         for i in list_sort (env['DEFINES'].keys ()):
454                 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
455         config.close ()
456 env.Command (config_hh, config_cache, config_header)
457
458 # hmm?
459 def xuniquify (lst):
460         n = []
461         for i in lst:
462                 if not i in n:
463                         n.append (i)
464         lst = n
465         return lst
466
467 def uniquify (lst):
468         d = {}
469         n = len (lst)
470         i = 0
471         while i < n:
472                 if not d.has_key (lst[i]):
473                         d[lst[i]] = 1
474                         i += 1
475                 else:
476                         del lst[i]
477                         n -= 1
478         return lst
479
480 def uniquify_config_vars (env):
481         for i in config_vars:
482                 if env.has_key (i) and type (env[i]) == type ([]):
483                         env[i] = uniquify (env[i])
484
485 def save_config_cache (env):
486         ## FIXME: Is this smart, using option cache for saving
487         ## config.cache?  I cannot seem to find the official method.
488         uniquify_config_vars (env)
489         opts.Save (config_cache, env)
490
491         if 'config' in COMMAND_LINE_TARGETS:
492                 sys.stdout.write ('\n')
493                 sys.stdout.write ('LilyPond configured')
494                 sys.stdout.write ('\n')
495                 sys.stdout.write ('now run')
496                 sys.stdout.write ('\n')
497                 sys.stdout.write ('    scons [TARGET|DIR]...')
498                 sys.stdout.write ('\n')
499                 Exit (0)
500         elif not env['checksums']:
501                 # When using timestams, config.hh is NEW.  The next
502                 # build triggers recompilation of everything.  Exiting
503                 # here makes SCons use the actual timestamp for config.hh
504                 # and prevents recompiling everything the next run.
505                 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
506                 sys.stdout.write ('Running %s ... ' % command)
507                 sys.stdout.write ('\n')
508                 s = os.system (command)
509                 Exit (s)
510
511
512 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
513         os.unlink (config_cache)
514 # WTF?
515 # scons: *** Calling Configure from Builders is not supported.
516 # env.Command (config_cache, None, configure)
517 if not os.path.exists (config_cache) \
518    or (os.stat ('SConstruct')[stat.ST_MTIME]
519        > os.stat (config_cache)[stat.ST_MTIME]):
520         env = configure (None, None, env)
521         save_config_cache (env)
522 elif env['checksums']:
523         # just save everything
524         save_config_cache (env)
525
526 #urg how does #/ subst work?
527 Export ('env')
528 SConscript ('buildscripts/builder.py')
529
530 env.PrependENVPath ('PATH',
531                     os.path.join (env['absbuild'], env['out'], 'usr/bin'))
532
533 if os.environ.has_key ('TEXMF'):
534         env.Append (ENV = {'TEXMF' : os.environ['TEXMF']})
535 env.Append (ENV = {
536         'TEXMF' : '{$LILYPONDPREFIX,' \
537         + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
538         'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond'),
539         })
540
541 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
542 BUILD_LILYPOND = '${set__x}$PYTHON $srcdir/scripts/lilypond.py${__verbose}'
543 BUILD_LILYPOND_BIN = '$absbuild/$out/lilypond-bin ${__verbose}'
544 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py --verbose'
545
546
547 # post-option environment-update
548 env.Append (
549         bindir = bindir,
550         sharedir = sharedir,
551         lilypond_datadir = sharedir_package,
552         localedir = localedir,
553         local_lilypond_datadir = sharedir_package_version,
554         lilypondprefix = lilypondprefix,
555         sharedir_package = sharedir_package,
556         sharedir_doc_package = sharedir_doc_package,
557         sharedir_package_version = sharedir_package_version,
558
559         LILYPOND = BUILD_LILYPOND,
560         ABC2LY = BUILD_ABC2LY,
561         LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
562         LILYPOND_BOOK_FORMAT = 'texi-html',
563         MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
564
565         TEXI2DVI_PAPERSIZE = '@afourpaper',
566         TEXI2DVI_FLAGS = [ '-t $TEXI2DVI_PAPERSIZE'],
567         DVIPS_PAPERSIZE = 'a4',
568         DVIPS_FLAGS = ['-t $DVIPS_PAPERSIZE',
569                        '-u+lilypond.map',
570                        '-u+ec-mftrace.map'],
571         PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
572         )
573
574 if env['debugging']:
575         env.Append (CCFLAGS = ['-g', '-pipe'])
576 if env['optimising']:
577         env.Append (CCFLAGS = '-O2')
578         env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
579 if env['warnings']:
580         env.Append (CCFLAGS = ['-W', '-Wall'])
581         env.Append (CXXFLAGS = ['-Wconversion'])
582
583 # ugr,huh?
584 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
585
586 if env['verbose']:
587         env['__verbose'] = ' --verbose'
588         env['set__x'] = 'set -x;'
589
590
591 ## Explicit target and dependencies
592
593 if 'clean' in COMMAND_LINE_TARGETS:
594         # ugh: prevent reconfigure instead of clean
595         os.system ('touch %s' % config_cache)
596         
597         command = sys.argv[0] + ' -c .'
598         sys.stdout.write ('Running %s ... ' % command)
599         sys.stdout.write ('\n')
600         s = os.system (command)
601         if os.path.exists (config_cache):
602                 os.unlink (config_cache)
603         Exit (s)
604
605 if 'sconsclean' in COMMAND_LINE_TARGETS:
606         command = 'rm -rf scons.cache $(find . -name ".scon*")'
607         s = os.system (command)
608         if os.path.exists (config_cache):
609                 os.unlink (config_cache)
610         Exit (s)
611         
612 if 'realclean' in COMMAND_LINE_TARGETS:
613         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
614         sys.stdout.write ('Running %s ... ' % command)
615         sys.stdout.write ('\n')
616         s = os.system (command)
617         if os.path.exists (config_cache):
618                 os.unlink (config_cache)
619         Exit (s)
620
621 # Declare SConscript phonies 
622 env.Alias ('minimal', config_cache)
623 env.Alias ('mf-essential', config_cache)
624
625 env.Alias ('minimal', ['lily', 'mf-essential'])
626 env.Alias ('all', ['minimal', 'mf', '.'])
627 # Do we want the doc/web separation?
628 env.Alias ('doc',
629            ['Documentation',
630             'Documentation/user',
631             'Documentation/topdocs',
632             'Documentation/bibliography',
633             'input'])
634
635 # Without target arguments, do minimal build
636 if not COMMAND_LINE_TARGETS:
637         env.Default (['minimal'])
638
639 # GNU Make rerouting compat:
640 env.Alias ('web', 'doc')
641
642
643 env.Command (version_hh, '#/VERSION',
644              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
645
646 # post-config environment update
647 env.Append (
648         run_prefix = run_prefix,
649         LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
650
651         # FIXME: move to lily/SConscript?
652         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),
653                    os.path.join (absbuild, 'kpath-guile', env['out']),
654                    os.path.join (absbuild, 'ttftool', env['out']),],
655         CPPPATH = [outdir, ],
656         LILYPOND_PATH = ['.', '$srcdir/input',
657                          '$srcdir/input/regression',
658                          '$srcdir/input/test',
659                          '$srcdir/input/tutorial',
660                          '$srcdir/Documentation/user',
661                          '$absbuild/mf/$out',
662 #                        os.path.join (absbuild, 'Documentation',
663 #                                      env['out']),
664 #                        os.path.join (absbuild, 'Documentation/user',
665 #                                      env['out']),
666                          ],
667         MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
668                          '$absbuild/Documentation/user/$out'],
669         )
670
671 def symlink_tree (target, source, env):
672         def mkdirs (dir):
673                 def mkdir (dir):
674                         if not dir:
675                                 os.chdir (os.sep)
676                                 return
677                         if not os.path.isdir (dir):
678                                 if os.path.exists (dir):
679                                         os.unlink (dir)
680                                 os.mkdir (dir)
681                         os.chdir (dir)
682                 map (mkdir, string.split (dir, os.sep))
683         def symlink (src, dst):
684                 os.chdir (absbuild)
685                 dir = os.path.dirname (dst)
686                 mkdirs (dir)
687                 if src[0] == '#':
688                         frm = os.path.join (srcdir, src[1:])
689                 else:
690                         depth = len (string.split (dir, '/'))
691                         if src.find ('@') > -1:
692                                 frm = os.path.join ('../' * depth,
693                                                     string.replace (src, '@',
694                                                                     env['out']))
695                         else:
696                                 frm = os.path.join ('../' * depth, src,
697                                                     env['out'])
698                 if src[-1] == '/':
699                         frm = os.path.join (frm, os.path.basename (dst))
700                 if env['verbose']:
701                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
702                 os.symlink (frm, os.path.basename (dst))
703         shutil.rmtree (run_prefix)
704         prefix = os.path.join (env['out'], 'usr')
705         map (lambda x: symlink (x[0], os.path.join (prefix,
706                                                     x[1] % {'ver' : version})),
707              # ^# := source dir
708              # @  := out
709              # /$ := add dst file_name
710              (('python',     'lib/lilypond/python'),
711               ('lily/',      'bin/lilypond'),
712               ('scripts/',   'bin/convert-ly'),
713               ('scripts/',   'bin/lilypond-book'),
714               ('scripts/',   'bin/ps2png'),
715               ('mf',         'share/lilypond/%(ver)s/dvips/mf-out'),
716               ('#ps',        'share/lilypond/%(ver)s/dvips/ps'),
717               ('#ps',        'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
718               ('mf',         'share/lilypond/%(ver)s/otf'),
719               ('mf',         'share/lilypond/%(ver)s/tfm'),
720               ('tex',        'share/lilypond/%(ver)s/tex/enc'),
721               ('#mf',        'share/lilypond/%(ver)s/fonts/mf'),
722               ('mf',         'share/lilypond/%(ver)s/fonts/map'),
723               ('mf',         'share/lilypond/%(ver)s/fonts/otf'),
724               ('mf',         'share/lilypond/%(ver)s/fonts/tfm'),
725               ('mf',         'share/lilypond/%(ver)s/fonts/type1'),
726               ('#tex',       'share/lilypond/%(ver)s/tex/source'),
727               ('tex',        'share/lilypond/%(ver)s/tex/tex-out'),
728               ('mf',         'share/lilypond/%(ver)s/tex/mf-out'),
729               ('#ly',        'share/lilypond/%(ver)s/ly'),
730               ('#scm',       'share/lilypond/%(ver)s/scm'),
731               ('#scripts',   'share/lilypond/%(ver)s/scripts'),
732               ('#ps',        'share/lilypond/%(ver)s/ps'),
733               ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
734               ('elisp',      'share/lilypond/%(ver)s/elisp')))
735         os.chdir (srcdir)
736
737 if env['debugging']:
738         stamp = os.path.join (run_prefix, 'stamp')
739         env.Command (stamp, 'SConstruct', [symlink_tree, 'touch $TARGET'])
740         env.Depends ('lily', stamp)
741         
742 #### dist, tar
743 def plus (a, b):
744         a + b
745
746 def cvs_entry_is_dir (line):
747         return line[0] == 'D' and line[-2] == '/'
748
749 def cvs_entry_is_file (line):
750         return line[0] == '/' and line[-2] == '/'
751
752 def cvs_dirs (dir):
753         ENTRIES = os.path.join (dir, 'CVS/Entries')
754         if not os.path.exists (ENTRIES):
755                 return []
756         entries = open (ENTRIES).readlines ()
757         dir_entries = filter (cvs_entry_is_dir, entries)
758         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
759                     dir_entries)
760         return dirs + map (cvs_dirs, dirs)
761
762 def cvs_files (dir):
763         ENTRIES = os.path.join (dir, 'CVS/Entries')
764         entries = open (ENTRIES).readlines ()
765         file_entries = filter (cvs_entry_is_file, entries)
766         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
767         return map (lambda x: os.path.join (dir, x), files)
768
769 def flatten (tree, lst):
770         if type (tree) == type ([]):
771                 for i in tree:
772                         if type (i) == type ([]):
773                                 flatten (i, lst)
774                         else:
775                                 lst.append (i)
776         return lst
777
778 if env['fast']\
779    and 'all' not in COMMAND_LINE_TARGETS\
780    and 'doc' not in COMMAND_LINE_TARGETS\
781    and 'web' not in COMMAND_LINE_TARGETS\
782    and 'install' not in COMMAND_LINE_TARGETS\
783    and 'clean' not in COMMAND_LINE_TARGETS:
784         subdirs = ['lily', 'lily/include',
785                    'flower', 'flower/include',
786                    'kpath-guile',
787                    'ttftool',
788                    'mf',
789                    ]
790 else:
791         subdirs = flatten (cvs_dirs ('.'), [])
792
793 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
794 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
795 txt_files = map (lambda x: x + '.txt', readme_files)
796
797
798 #
799 # speeds up build by +- 5% 
800
801 if not env['fast']:
802         foo = map (lambda x: env.TXT (x + '.txt',
803                                       os.path.join ('Documentation/topdocs', x)),
804                    readme_files)
805         tar_base = package.name + '-' + version
806         tar_name = tar_base + '.tar.gz'
807         ball_prefix = os.path.join (outdir, tar_base)
808         tar_ball = os.path.join (outdir, tar_name)
809
810         dist_files = src_files + txt_files
811         ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
812         map (lambda x: env.Depends (tar_ball, x), ball_files)
813         map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
814                                     'ln $SOURCE $TARGET'), dist_files)
815         tar = env.Command (tar_ball, src_files,
816                            ['rm -f $$(find $TARGET.dir -name .sconsign)',
817                             'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
818         env.Alias ('tar', tar)
819
820         dist_ball = os.path.join (package.release_dir, tar_name)
821         env.Command (dist_ball, tar_ball,
822                      'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
823                      + 'ln $SOURCE $TARGET')
824         env.Depends ('dist', dist_ball)
825         patch_name = os.path.join (outdir, tar_base + '.diff.gz')
826         patch = env.PATCH (patch_name, tar_ball)
827         env.Depends (patch_name, dist_ball)
828         env.Alias ('release', patch)
829
830 #### web
831 if not env['fast']:
832         web_base = os.path.join (outdir, 'web')
833         web_ball = web_base + '.tar.gz'
834         env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
835         web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
836         web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"')
837         env['web_path'] = web_path
838         web_list = os.path.join (outdir, 'weblist')
839         # compatible make heritits
840         # fixme: generate in $outdir is cwd/builddir
841         env.Command (web_list,
842                      ## this is correct, but takes > 5min if you have a peder :-)
843                      ##'doc',
844                      '#/VERSION',
845                      ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
846                       'cd $absbuild && $footify $$(find . -name "*.html" -print)',
847                       'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
848                       'cd $absbuild && find Documentation input $web_path \
849                       > $TARGET',
850                       '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
851                       '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
852                       'cd $absbuild && ls *.html >> $TARGET',])
853         env.Command (web_ball, web_list,
854                      ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
855         #env.Alias ('web', web_ball)
856         www_base = os.path.join (outdir, 'www')
857         www_ball = www_base + '.tar.gz'
858         env.Command (www_ball, web_ball,
859                      ['rm -rf $out/tmp',
860                       'mkdir -p $absbuild/$out/tmp',
861                       'tar -C $absbuild/$out/tmp -xzf $SOURCE',
862                       'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
863                       + ' do mv $$i $$(dirname $$i)/out-www; done',
864                       'tar -C $absbuild/$out/tmp -czf $TARGET .'])
865         env.Alias ('web', www_ball)
866
867 #### tags
868 env.Append (
869         ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
870         --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
871 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
872 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
873                              src_files),
874              'etags $ETAGSFLAGS $SOURCES')
875
876 # Note: SConscripts are only needed in directories where something needs
877 # to be done, building or installing
878 for d in subdirs:
879         if os.path.exists (os.path.join (d, 'SConscript')):
880                 b = os.path.join (env['build'], d, env['out'])
881                 # Support clean sourcetree build (--srcdir build)
882                 # and ./out build.
883                 if os.path.abspath (b) != os.path.abspath (d):
884                         env.BuildDir (b, d, duplicate = 0)
885                 SConscript (os.path.join (b, 'SConscript'))
886