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