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