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