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