]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* SConstruct: Require pkg-config.
[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]*).*$', re.DOTALL)
239         def get_version (program):
240                 command = '(pkg-config --modversion %(program)s || %(program)s --version || %(program)s -V) 2>&1' % vars ()
241                 pipe = os.popen (command)
242                 output = pipe.read ()
243                 if pipe.close ():
244                         return None
245                 v = re.sub (vre, '\\1', output)
246                 if v[-1] == '\n':
247                         v = v[:-1]
248                 return string.split (v, '.')
249
250         def test_program (lst, program, minimal, description, package):
251                 sys.stdout.write ('Checking %s version... ' % program)
252                 actual = get_version (program)
253                 if not actual:
254                         print 'not found'
255                         lst.append ((description, package, minimal, program,
256                                      'not installed'))
257                         return 0
258                 sys.stdout.write (string.join (actual, '.'))
259                 sys.stdout.write ('\n')
260                 if actual < string.split (minimal, '.'):
261                         lst.append ((description, package, minimal, program,
262                                      string.join (actual, '.')))
263                         return 0
264                 return 1
265
266         def test_lib (lst, program, minimal, description):
267                 # FIXME: test for Debian or RPM (or -foo?) based dists
268                 # to guess (or get correct!: apt-cache search?)
269                 # package name.
270                 #if os.system ('pkg-config --atleast-version=0 freetype2'):
271                 # barf
272                 if test_program (lst, program, minimal, description,
273                                  'lib%(program)s-dev or %(program)s-devel'
274                                  % vars ()):
275                         env.ParseConfig ('pkg-config --cflags --libs %(program)s'
276                                          % vars ())
277                         return 1
278                 return 0
279
280         for i in ['bash', 'perl', 'python', 'sh']:
281                 sys.stdout.write ('Checking for %s... ' % i)
282                 c = WhereIs (i)
283                 key = string.upper (i)
284                 if c:
285                         env[key] = c
286                         sys.stdout.write (c)
287                 else:
288                         sys.stdout.write ('not found: %s (using: %s)' \
289                                           % (c, env[key]))
290                         # Hmm? abort?
291                 sys.stdout.write ('\n')
292
293         required = []
294         test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
295         test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
296         test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
297         test_program (required, 'guile-config', '1.6', 'GUILE development',
298                         'libguile-dev or guile-devel')
299         # Do not use bison 1.50 and 1.75.
300         test_program (required, 'bison', '1.25', 'Bison -- parser generator',
301                         'bison')
302         test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
303
304         optional = []
305         test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
306         test_program (optional, 'guile', '1.6', 'GUILE scheme', 'guile')
307         test_program (optional, 'mftrace', '1.1.0', 'Metafont tracing Type1',
308                         'mftrace')
309         test_program (optional, 'perl', '4.0',
310                         'Perl practical efficient readonly language', 'perl')
311         #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
312
313         def CheckYYCurrentBuffer (context):
314                 context.Message ('Checking for yy_current_buffer... ')
315                 ret = conf.TryCompile ("""using namespace std;
316                 #include <FlexLexer.h>
317                 class yy_flex_lexer: public yyFlexLexer
318                 {
319                 public:
320                 yy_flex_lexer ()
321                 {
322                 yy_current_buffer = 0;
323                 }
324                 };""", '.cc')
325                 context.Result (ret)
326                 return ret
327
328         conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
329                                                 : CheckYYCurrentBuffer })
330
331         defines = {
332            'DIRSEP' : "'%s'" % os.sep,
333            'PATHSEP' : "'%s'" % os.pathsep,
334            'PACKAGE': '"%s"' % package.name,
335            'DATADIR' : '"%s"' % sharedir,
336            'PACKAGE_DATADIR' : '"%s"' % sharedir_package,
337            'LOCALEDIR' : '"%s"' %localedir,
338         }
339         conf.env.Append (DEFINES = defines)
340
341         command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
342         PYTHON_INCLUDE = os.popen (command).read ()
343         if env['fast']:
344                 env.Append (CCFLAGS = ['-I%s ' % PYTHON_INCLUDE])
345         else:
346                 env.Append (CPPPATH = PYTHON_INCLUDE)
347
348         headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'libio.h',
349                    'Python.h')
350         for i in headers:
351                 if conf.CheckCHeader (i):
352                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
353                         conf.env['DEFINES'][key] = 1
354
355         ccheaders = ('sstream',)
356         for i in ccheaders:
357                 if conf.CheckCXXHeader (i):
358                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
359                         conf.env['DEFINES'][key] = 1
360
361         functions = ('fopencookie', 'funopen',
362                      'gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
363         for i in functions:
364                 if 0 or conf.CheckFunc (i):
365                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
366                         conf.env['DEFINES'][key] = 1
367
368         if conf.CheckYYCurrentBuffer ():
369                 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
370
371         if conf.CheckLib ('dl'):
372                 pass
373
374         if conf.CheckLib ('kpathsea'):
375                 conf.env['DEFINES']['KPATHSEA'] = 1
376
377         # huh? 
378         if conf.CheckLib ('kpathsea', 'kpse_find_file'):
379                 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
380         if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
381                 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
382
383         # FIXME fc3 - move to kpath-guile/SConscript?
384         conf.env['DEFINES']['HAVE_LIBKPATHSEA_SO'] = '1'
385
386         if env['fast']:
387                 cpppath = []
388                 if env.has_key ('CPPPATH'):
389                         cpppath = env['CPPPATH']
390
391         ## FIXME: linkage, check for libguile.h and scm_boot_guile
392         #this could happen after flower...
393         env.ParseConfig ('guile-config compile')
394
395         test_program (required, 'pkg-config', '0.9.0',
396                       'pkg-config library compile manager', 'pkg-config')
397         if test_lib (required, 'freetype2', '0.0',
398                      'Development files for FreeType 2 font engine'):
399                 conf.env['DEFINES']['HAVE_FREETYPE2'] = '1'
400                 
401         if test_lib (required, 'pangoft2', '1.6.0',
402                      'Development files for pango, with fontconfig2'):
403                 conf.env['DEFINES']['HAVE_PANGO_FT2'] = '1'
404                 conf.env['DEFINES']['HAVE_PANGO16'] = '1'
405
406         if test_lib (optional, 'fontconfig', '2.2.0',
407                      'Development files for fontconfig'):
408                 conf.env['DEFINES']['HAVE_FONTCONFIG'] = '1'
409         
410         #this could happen only for compiling pango-*
411         if env['gui']:
412                 test_lib (required, 'gtk+-2.0', '2.4.0',
413                           'Development files for GTK+')
414                 if test_lib (required, 'pango', '1.6.0',
415                           'Development files for pango'):
416                         conf.env['DEFINES']['HAVE_PANGO16'] = '1'
417                         
418                 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
419                         conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
420                 if conf.CheckLib ('pango-1.0',
421                                   'pango_fc_font_map_add_decoder_find_func'):
422                         conf.env['DEFINES']['HAVE_PANGO16'] = '1'
423                         conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
424
425         if env['fast']:
426                 # Using CCFLAGS = -I<system-dir> rather than CPPPATH = [
427                 # <system-dir>] speeds up SCons
428                 env['CCFLAGS'] += map (lambda x: '-I' + x,
429                                        env['CPPPATH'][len (cpppath):])
430                 env['CPPPATH'] = cpppath
431
432         if required:
433                 print
434                 print '********************************'
435                 print 'Please install required packages'
436                 for i in required:
437                         print '%s:      %s-%s or newer (found: %s %s)' % i
438                 Exit (1)
439
440         if optional:
441                 print
442                 print '*************************************'
443                 print 'Consider installing optional packages'
444                 for i in optional:
445                         print '%s:      %s-%s or newer (found: %s %s)' % i
446
447         return conf.Finish ()
448
449 def config_header (target, source, env):
450         config = open (str (target[0]), 'w')
451         for i in list_sort (env['DEFINES'].keys ()):
452                 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
453         config.close ()
454 env.Command (config_hh, config_cache, config_header)
455
456 # hmm?
457 def xuniquify (lst):
458         n = []
459         for i in lst:
460                 if not i in n:
461                         n.append (i)
462         lst = n
463         return lst
464
465 def uniquify (lst):
466         d = {}
467         n = len (lst)
468         i = 0
469         while i < n:
470                 if not d.has_key (lst[i]):
471                         d[lst[i]] = 1
472                         i += 1
473                 else:
474                         del lst[i]
475                         n -= 1
476         return lst
477
478 def uniquify_config_vars (env):
479         for i in config_vars:
480                 if env.has_key (i) and type (env[i]) == type ([]):
481                         env[i] = uniquify (env[i])
482
483 def save_config_cache (env):
484         ## FIXME: Is this smart, using option cache for saving
485         ## config.cache?  I cannot seem to find the official method.
486         uniquify_config_vars (env)
487         opts.Save (config_cache, env)
488
489         if 'config' in COMMAND_LINE_TARGETS:
490                 sys.stdout.write ('\n')
491                 sys.stdout.write ('LilyPond configured')
492                 sys.stdout.write ('\n')
493                 sys.stdout.write ('now run')
494                 sys.stdout.write ('\n')
495                 sys.stdout.write ('    scons [TARGET|DIR]...')
496                 sys.stdout.write ('\n')
497                 Exit (0)
498         elif not env['checksums']:
499                 # When using timestams, config.hh is NEW.  The next
500                 # build triggers recompilation of everything.  Exiting
501                 # here makes SCons use the actual timestamp for config.hh
502                 # and prevents recompiling everything the next run.
503                 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
504                 sys.stdout.write ('Running %s ... ' % command)
505                 sys.stdout.write ('\n')
506                 s = os.system (command)
507                 Exit (s)
508
509
510 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
511         os.unlink (config_cache)
512 # WTF?
513 # scons: *** Calling Configure from Builders is not supported.
514 # env.Command (config_cache, None, configure)
515 if not os.path.exists (config_cache) \
516    or (os.stat ('SConstruct')[stat.ST_MTIME]
517        > os.stat (config_cache)[stat.ST_MTIME]):
518         env = configure (None, None, env)
519         save_config_cache (env)
520 elif env['checksums']:
521         # just save everything
522         save_config_cache (env)
523
524 #urg how does #/ subst work?
525 Export ('env')
526 SConscript ('buildscripts/builder.py')
527
528 env.PrependENVPath ('PATH',
529                     os.path.join (env['absbuild'], env['out'], 'usr/bin'))
530
531 if os.environ.has_key ('TEXMF'):
532         env.Append (ENV = {'TEXMF' : os.environ['TEXMF']})
533 env.Append (ENV = {
534         'TEXMF' : '{$LILYPONDPREFIX,' \
535         + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
536         'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond'),
537         })
538
539 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
540 BUILD_LILYPOND = '${set__x}$PYTHON $srcdir/scripts/lilypond.py${__verbose}'
541 BUILD_LILYPOND_BIN = '$absbuild/$out/lilypond-bin ${__verbose}'
542 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py --verbose'
543
544
545 # post-option environment-update
546 env.Append (
547         bindir = bindir,
548         sharedir = sharedir,
549         lilypond_datadir = sharedir_package,
550         localedir = localedir,
551         local_lilypond_datadir = sharedir_package_version,
552         lilypondprefix = lilypondprefix,
553         sharedir_package = sharedir_package,
554         sharedir_doc_package = sharedir_doc_package,
555         sharedir_package_version = sharedir_package_version,
556
557         LILYPOND = BUILD_LILYPOND,
558         ABC2LY = BUILD_ABC2LY,
559         LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
560         LILYPOND_BOOK_FORMAT = 'texi-html',
561         MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
562
563         TEXI2DVI_PAPERSIZE = '@afourpaper',
564         TEXI2DVI_FLAGS = [ '-t $TEXI2DVI_PAPERSIZE'],
565         DVIPS_PAPERSIZE = 'a4',
566         DVIPS_FLAGS = ['-t $DVIPS_PAPERSIZE',
567                        '-u+lilypond.map',
568                        '-u+ec-mftrace.map'],
569         PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
570         )
571
572 if env['debugging']:
573         env.Append (CCFLAGS = ['-g', '-pipe'])
574 if env['optimising']:
575         env.Append (CCFLAGS = '-O2')
576         env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
577 if env['warnings']:
578         env.Append (CCFLAGS = ['-W', '-Wall'])
579         env.Append (CXXFLAGS = ['-Wconversion'])
580
581 # ugr,huh?
582 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
583
584 if env['verbose']:
585         env['__verbose'] = ' --verbose'
586         env['set__x'] = 'set -x;'
587
588
589 ## Explicit target and dependencies
590
591 if 'clean' in COMMAND_LINE_TARGETS:
592         # ugh: prevent reconfigure instead of clean
593         os.system ('touch %s' % config_cache)
594         
595         command = sys.argv[0] + ' -c .'
596         sys.stdout.write ('Running %s ... ' % command)
597         sys.stdout.write ('\n')
598         s = os.system (command)
599         if os.path.exists (config_cache):
600                 os.unlink (config_cache)
601         Exit (s)
602
603 if 'sconsclean' in COMMAND_LINE_TARGETS:
604         command = 'rm -rf scons.cache $(find . -name ".scon*")'
605         s = os.system (command)
606         if os.path.exists (config_cache):
607                 os.unlink (config_cache)
608         Exit (s)
609         
610 if 'realclean' in COMMAND_LINE_TARGETS:
611         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
612         sys.stdout.write ('Running %s ... ' % command)
613         sys.stdout.write ('\n')
614         s = os.system (command)
615         if os.path.exists (config_cache):
616                 os.unlink (config_cache)
617         Exit (s)
618
619 # Declare SConscript phonies 
620 env.Alias ('minimal', config_cache)
621 env.Alias ('mf-essential', config_cache)
622
623 env.Alias ('minimal', ['lily', 'mf-essential'])
624 env.Alias ('all', ['minimal', 'mf', '.'])
625 # Do we want the doc/web separation?
626 env.Alias ('doc',
627            ['Documentation',
628             'Documentation/user',
629             'Documentation/topdocs',
630             'Documentation/bibliography',
631             'input'])
632
633 # Without target arguments, do minimal build
634 if not COMMAND_LINE_TARGETS:
635         env.Default (['minimal'])
636
637 # GNU Make rerouting compat:
638 env.Alias ('web', 'doc')
639
640
641 env.Command (version_hh, '#/VERSION',
642              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
643
644 # post-config environment update
645 env.Append (
646         run_prefix = run_prefix,
647         LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
648
649         # FIXME: move to lily/SConscript?
650         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),
651                    os.path.join (absbuild, 'kpath-guile', env['out']),
652                    os.path.join (absbuild, 'ttftool', env['out']),],
653         CPPPATH = [outdir, ],
654         LILYPOND_PATH = ['.', '$srcdir/input',
655                          '$srcdir/input/regression',
656                          '$srcdir/input/test',
657                          '$srcdir/input/tutorial',
658                          '$srcdir/Documentation/user',
659                          '$absbuild/mf/$out',
660 #                        os.path.join (absbuild, 'Documentation',
661 #                                      env['out']),
662 #                        os.path.join (absbuild, 'Documentation/user',
663 #                                      env['out']),
664                          ],
665         MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
666                          '$absbuild/Documentation/user/$out'],
667         )
668
669 def symlink_tree (target, source, env):
670         def mkdirs (dir):
671                 def mkdir (dir):
672                         if not dir:
673                                 os.chdir (os.sep)
674                                 return
675                         if not os.path.isdir (dir):
676                                 if os.path.exists (dir):
677                                         os.unlink (dir)
678                                 os.mkdir (dir)
679                         os.chdir (dir)
680                 map (mkdir, string.split (dir, os.sep))
681         def symlink (src, dst):
682                 os.chdir (absbuild)
683                 dir = os.path.dirname (dst)
684                 mkdirs (dir)
685                 if src[0] == '#':
686                         frm = os.path.join (srcdir, src[1:])
687                 else:
688                         depth = len (string.split (dir, '/'))
689                         if src.find ('@') > -1:
690                                 frm = os.path.join ('../' * depth,
691                                                     string.replace (src, '@',
692                                                                     env['out']))
693                         else:
694                                 frm = os.path.join ('../' * depth, src,
695                                                     env['out'])
696                 if src[-1] == '/':
697                         frm = os.path.join (frm, os.path.basename (dst))
698                 if env['verbose']:
699                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
700                 os.symlink (frm, os.path.basename (dst))
701         shutil.rmtree (run_prefix)
702         prefix = os.path.join (env['out'], 'usr')
703         map (lambda x: symlink (x[0], os.path.join (prefix,
704                                                     x[1] % {'ver' : version})),
705              # ^# := source dir
706              # @  := out
707              # /$ := add dst file_name
708              (('python',     'lib/lilypond/python'),
709               ('lily/',      'bin/lilypond'),
710               ('scripts/',   'bin/convert-ly'),
711               ('scripts/',   'bin/lilypond-book'),
712               ('scripts/',   'bin/ps2png'),
713               ('mf',         'share/lilypond/%(ver)s/dvips/mf-out'),
714               ('#ps',        'share/lilypond/%(ver)s/dvips/ps'),
715               ('#ps',        'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
716               ('mf',         'share/lilypond/%(ver)s/otf'),
717               ('mf',         'share/lilypond/%(ver)s/tfm'),
718               ('tex',        'share/lilypond/%(ver)s/tex/enc'),
719               ('#mf',        'share/lilypond/%(ver)s/fonts/mf'),
720               ('mf',         'share/lilypond/%(ver)s/fonts/map'),
721               ('mf',         'share/lilypond/%(ver)s/fonts/otf'),
722               ('mf',         'share/lilypond/%(ver)s/fonts/tfm'),
723               ('mf',         'share/lilypond/%(ver)s/fonts/type1'),
724               ('#tex',       'share/lilypond/%(ver)s/tex/source'),
725               ('tex',        'share/lilypond/%(ver)s/tex/tex-out'),
726               ('mf',         'share/lilypond/%(ver)s/tex/mf-out'),
727               ('#ly',        'share/lilypond/%(ver)s/ly'),
728               ('#scm',       'share/lilypond/%(ver)s/scm'),
729               ('#scripts',   'share/lilypond/%(ver)s/scripts'),
730               ('#ps',        'share/lilypond/%(ver)s/ps'),
731               ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
732               ('elisp',      'share/lilypond/%(ver)s/elisp')))
733         os.chdir (srcdir)
734
735 if env['debugging']:
736         stamp = os.path.join (run_prefix, 'stamp')
737         env.Command (stamp, 'SConstruct', [symlink_tree, 'touch $TARGET'])
738         env.Depends ('lily', stamp)
739         
740 #### dist, tar
741 def plus (a, b):
742         a + b
743
744 def cvs_entry_is_dir (line):
745         return line[0] == 'D' and line[-2] == '/'
746
747 def cvs_entry_is_file (line):
748         return line[0] == '/' and line[-2] == '/'
749
750 def cvs_dirs (dir):
751         ENTRIES = os.path.join (dir, 'CVS/Entries')
752         if not os.path.exists (ENTRIES):
753                 return []
754         entries = open (ENTRIES).readlines ()
755         dir_entries = filter (cvs_entry_is_dir, entries)
756         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
757                     dir_entries)
758         return dirs + map (cvs_dirs, dirs)
759
760 def cvs_files (dir):
761         ENTRIES = os.path.join (dir, 'CVS/Entries')
762         entries = open (ENTRIES).readlines ()
763         file_entries = filter (cvs_entry_is_file, entries)
764         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
765         return map (lambda x: os.path.join (dir, x), files)
766
767 def flatten (tree, lst):
768         if type (tree) == type ([]):
769                 for i in tree:
770                         if type (i) == type ([]):
771                                 flatten (i, lst)
772                         else:
773                                 lst.append (i)
774         return lst
775
776 if env['fast']\
777    and 'all' not in COMMAND_LINE_TARGETS\
778    and 'doc' not in COMMAND_LINE_TARGETS\
779    and 'web' not in COMMAND_LINE_TARGETS\
780    and 'install' not in COMMAND_LINE_TARGETS\
781    and 'clean' not in COMMAND_LINE_TARGETS:
782         subdirs = ['lily', 'lily/include',
783                    'flower', 'flower/include',
784                    'kpath-guile',
785                    'ttftool',
786                    'mf',
787                    ]
788 else:
789         subdirs = flatten (cvs_dirs ('.'), [])
790
791 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
792 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
793 txt_files = map (lambda x: x + '.txt', readme_files)
794
795
796 #
797 # speeds up build by +- 5% 
798
799 if not env['fast']:
800         foo = map (lambda x: env.TXT (x + '.txt',
801                                       os.path.join ('Documentation/topdocs', x)),
802                    readme_files)
803         tar_base = package.name + '-' + version
804         tar_name = tar_base + '.tar.gz'
805         ball_prefix = os.path.join (outdir, tar_base)
806         tar_ball = os.path.join (outdir, tar_name)
807
808         dist_files = src_files + txt_files
809         ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
810         map (lambda x: env.Depends (tar_ball, x), ball_files)
811         map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
812                                     'ln $SOURCE $TARGET'), dist_files)
813         tar = env.Command (tar_ball, src_files,
814                            ['rm -f $$(find $TARGET.dir -name .sconsign)',
815                             'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
816         env.Alias ('tar', tar)
817
818         dist_ball = os.path.join (package.release_dir, tar_name)
819         env.Command (dist_ball, tar_ball,
820                      'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
821                      + 'ln $SOURCE $TARGET')
822         env.Depends ('dist', dist_ball)
823         patch_name = os.path.join (outdir, tar_base + '.diff.gz')
824         patch = env.PATCH (patch_name, tar_ball)
825         env.Depends (patch_name, dist_ball)
826         env.Alias ('release', patch)
827
828 #### web
829 if not env['fast']:
830         web_base = os.path.join (outdir, 'web')
831         web_ball = web_base + '.tar.gz'
832         env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
833         web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
834         web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"')
835         env['web_path'] = web_path
836         web_list = os.path.join (outdir, 'weblist')
837         # compatible make heritits
838         # fixme: generate in $outdir is cwd/builddir
839         env.Command (web_list,
840                      ## this is correct, but takes > 5min if you have a peder :-)
841                      ##'doc',
842                      '#/VERSION',
843                      ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
844                       'cd $absbuild && $footify $$(find . -name "*.html" -print)',
845                       'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
846                       'cd $absbuild && find Documentation input $web_path \
847                       > $TARGET',
848                       '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
849                       '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
850                       'cd $absbuild && ls *.html >> $TARGET',])
851         env.Command (web_ball, web_list,
852                      ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
853         #env.Alias ('web', web_ball)
854         www_base = os.path.join (outdir, 'www')
855         www_ball = www_base + '.tar.gz'
856         env.Command (www_ball, web_ball,
857                      ['rm -rf $out/tmp',
858                       'mkdir -p $absbuild/$out/tmp',
859                       'tar -C $absbuild/$out/tmp -xzf $SOURCE',
860                       'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
861                       + ' do mv $$i $$(dirname $$i)/out-www; done',
862                       'tar -C $absbuild/$out/tmp -czf $TARGET .'])
863         env.Alias ('web', www_ball)
864
865 #### tags
866 env.Append (
867         ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
868         --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
869 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
870 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
871                              src_files),
872              'etags $ETAGSFLAGS $SOURCES')
873
874 # Note: SConscripts are only needed in directories where something needs
875 # to be done, building or installing
876 for d in subdirs:
877         if os.path.exists (os.path.join (d, 'SConscript')):
878                 b = os.path.join (env['build'], d, env['out'])
879                 # Support clean sourcetree build (--srcdir build)
880                 # and ./out build.
881                 if os.path.abspath (b) != os.path.abspath (d):
882                         env.BuildDir (b, d, duplicate = 0)
883                 SConscript (os.path.join (b, 'SConscript'))
884