]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* lily/SConscript: Remove ttftools.
[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.TryLink ("""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
692 if env['verbose']:
693         env['__verbose'] = ' --verbose'
694         env['set__x'] = 'set -x;'
695
696
697 ## Explicit target and dependencies
698
699 if 'clean' in COMMAND_LINE_TARGETS:
700         # ugh: prevent reconfigure instead of clean
701         os.system ('touch %s' % config_cache)
702         
703         command = sys.argv[0] + ' -c .'
704         sys.stdout.write ('Running %s ... ' % command)
705         sys.stdout.write ('\n')
706         s = os.system (command)
707         if os.path.exists (config_cache):
708                 os.unlink (config_cache)
709         Exit (s)
710
711 if 'sconsclean' in COMMAND_LINE_TARGETS:
712         command = 'rm -rf scons.cache $(find . -name ".scon*")'
713         s = os.system (command)
714         if os.path.exists (config_cache):
715                 os.unlink (config_cache)
716         Exit (s)
717         
718 if 'realclean' in COMMAND_LINE_TARGETS:
719         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
720         sys.stdout.write ('Running %s ... ' % command)
721         sys.stdout.write ('\n')
722         s = os.system (command)
723         if os.path.exists (config_cache):
724                 os.unlink (config_cache)
725         Exit (s)
726
727 # Declare SConscript phonies 
728 env.Alias ('minimal', config_cache)
729 env.Alias ('mf-essential', config_cache)
730
731 env.Alias ('minimal', ['lily', 'mf-essential'])
732 env.Alias ('all', ['minimal', 'mf', '.'])
733 # Do we want the doc/web separation?
734 env.Alias ('doc',
735            ['Documentation',
736             'Documentation/user',
737             'Documentation/topdocs',
738             'Documentation/bibliography',
739             'input'])
740
741 # Without target arguments, do minimal build
742 if not COMMAND_LINE_TARGETS:
743         env.Default (['minimal'])
744
745 # GNU Make rerouting compat:
746 env.Alias ('web', 'doc')
747
748
749 env.Command (version_hh, '#/VERSION',
750              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
751
752 # post-config environment update
753 env.Append (
754         run_prefix = run_prefix,
755         LILYPONDPREFIX = LILYPONDPREFIX,
756
757         # FIXME: move to lily/SConscript?
758         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),
759                    os.path.join (absbuild, 'kpath-guile', env['out']),],
760         CPPPATH = [outdir, ],
761         LILYPOND_PATH = ['.',
762                          '$srcdir/input',
763                          '$srcdir/input/regression',
764                          '$srcdir/input/test',
765                          '$srcdir/input/tutorial',
766                          '$srcdir/Documentation/user',
767                          '$absbuild/mf/$out',
768 #                        os.path.join (absbuild, 'Documentation',
769 #                                      env['out']),
770 #                        os.path.join (absbuild, 'Documentation/user',
771 #                                      env['out']),
772                          ],
773         makeinfo_path = ['.', '$srcdir/Documentation/user',
774                          '$absbuild/Documentation/user/$out'],
775         )
776
777 def symlink_tree (target, source, env):
778         def mkdirs (dir):
779                 def mkdir (dir):
780                         if not dir:
781                                 os.chdir (os.sep)
782                                 return
783                         if not os.path.isdir (dir):
784                                 if os.path.exists (dir):
785                                         os.unlink (dir)
786                                 os.mkdir (dir)
787                         os.chdir (dir)
788                 map (mkdir, string.split (dir, os.sep))
789         def symlink (src, dst):
790                 os.chdir (absbuild)
791                 dir = os.path.dirname (dst)
792                 mkdirs (dir)
793                 if src[0] == '#':
794                         frm = os.path.join (srcdir, src[1:])
795                 else:
796                         depth = len (string.split (dir, '/'))
797                         if src.find ('@') > -1:
798                                 frm = os.path.join ('../' * depth,
799                                                     string.replace (src, '@',
800                                                                     env['out']))
801                         else:
802                                 frm = os.path.join ('../' * depth, src,
803                                                     env['out'])
804                 if src[-1] == '/':
805                         frm = os.path.join (frm, os.path.basename (dst))
806                 if env['verbose']:
807                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
808                 os.symlink (frm, os.path.basename (dst))
809         shutil.rmtree (run_prefix)
810         prefix = os.path.join (env['out'], 'usr')
811         map (lambda x: symlink (x[0], os.path.join (prefix,
812                                                     x[1] % {'ver' : version})),
813              # ^# := source dir
814              # @  := out
815              # /$ := add dst file_name
816              (('python',     'lib/lilypond/python'),
817               # ugh
818               ('python',     'share/lilypond/%(ver)s/python'),
819               ('lily/',      'bin/lilypond'),
820               ('scripts/',   'bin/convert-ly'),
821               ('scripts/',   'bin/lilypond-book'),
822               ('scripts/',   'bin/ps2png'),
823               ('mf',         'share/lilypond/%(ver)s/dvips/mf-out'),
824               ('#ps',        'share/lilypond/%(ver)s/dvips/ps'),
825               ('#ps',        'share/lilypond/%(ver)s/tex/music-drawing-routines.ps'),
826               ('mf',         'share/lilypond/%(ver)s/otf'),
827               ('mf',         'share/lilypond/%(ver)s/tfm'),
828               ('tex',        'share/lilypond/%(ver)s/tex/enc'),
829               ('#mf',        'share/lilypond/%(ver)s/fonts/mf'),
830               ('mf',         'share/lilypond/%(ver)s/fonts/map'),
831               ('mf',         'share/lilypond/%(ver)s/fonts/otf'),
832               ('mf',         'share/lilypond/%(ver)s/fonts/tfm'),
833               ('mf',         'share/lilypond/%(ver)s/fonts/type1'),
834               ('#tex',       'share/lilypond/%(ver)s/tex/source'),
835               ('tex',        'share/lilypond/%(ver)s/tex/tex-out'),
836               ('mf',         'share/lilypond/%(ver)s/tex/mf-out'),
837               ('#ly',        'share/lilypond/%(ver)s/ly'),
838               ('#scm',       'share/lilypond/%(ver)s/scm'),
839               ('#scripts',   'share/lilypond/%(ver)s/scripts'),
840               ('#ps',        'share/lilypond/%(ver)s/ps'),
841               ('po/@/nl.mo', 'share/locale/nl/lc_messages/lilypond.mo'),
842               ('elisp',      'share/lilypond/%(ver)s/elisp')))
843
844         print "fixme: barf barf barf"
845         os.chdir (absbuild)
846         out = env['out']
847         ver = version
848         prefix = os.path.join (env['out'], 'usr/share/lilypond/%(ver)s/fonts'
849                                % vars ())
850         for ext in ('enc', 'map', 'otf', 'svg', 'tfm', 'pfa'):
851                 dir = os.path.join (absbuild, prefix, ext)
852                 os.system ('rm -f ' + dir)
853                 mkdirs (dir)
854                 os.chdir (dir)
855                 os.system ('ln -s ../../../../../../../mf/%(out)s/*.%(ext)s .'
856                            % vars ())
857         os.chdir (srcdir)
858
859 if env['debugging']:
860         stamp = os.path.join (run_prefix, 'stamp')
861         env.command (stamp, ['#/SConstruct', '#/VERSION'],
862                      [symlink_tree, 'touch $target'])
863         env.depends ('lily', stamp)
864         
865 #### dist, tar
866 def plus (a, b):
867         a + b
868
869 def cvs_entry_is_dir (line):
870         return line[0] == 'D' and line[-2] == '/'
871
872 def cvs_entry_is_file (line):
873         return line[0] == '/' and line[-2] == '/'
874
875 def cvs_dirs (dir):
876         entries = os.path.join (dir, 'CVS/Entries')
877         if not os.path.exists (entries):
878                 return []
879         entries = open (entries).readlines ()
880         dir_entries = filter (cvs_entry_is_dir, entries)
881         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
882                     dir_entries)
883         return dirs + map (cvs_dirs, dirs)
884
885 def cvs_files (dir):
886         entries = os.path.join (dir, 'CVS/Entries')
887         if not os.path.exists (entries):
888                 return []
889         entries = open (entries).readlines ()
890         file_entries = filter (cvs_entry_is_file, entries)
891         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
892         return map (lambda x: os.path.join (dir, x), files)
893
894 def flatten (tree, lst):
895         if type (tree) == type ([]):
896                 for i in tree:
897                         if type (i) == type ([]):
898                                 flatten (i, lst)
899                         else:
900                                 lst.append (i)
901         return lst
902
903 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
904         subdirs = flatten (cvs_dirs ('.'), [])
905 else:
906         # ugh
907         command = 'cd %(srcdir)s \
908         && find . -name SConscript | sed s@/SConscript@@' % vars ()
909         subdirs = string.split (os.popen (command).read ())
910
911 if env['fast']\
912    and 'all' not in command_line_targets\
913    and 'doc' not in command_line_targets\
914    and 'web' not in command_line_targets\
915    and 'install' not in command_line_targets\
916    and 'clean' not in command_line_targets:
917         subdirs = ['lily', 'lily/include',
918                    'flower', 'flower/include',
919                    'kpath-guile',
920                    'mf',
921                    ]
922
923 if os.path.isdir ('%(srcdir)s/CVS' % vars ()):
924         src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
925 else:
926         src_files = ['foobar']
927
928 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
929 txt_files = map (lambda x: x + '.txt', readme_files)
930
931
932 #
933 # speeds up build by +- 5% 
934
935 if not env['fast']:
936         foo = map (lambda x: env.TXT (x + '.txt',
937                                       os.path.join ('Documentation/topdocs', x)),
938                    readme_files)
939         tar_base = package.name + '-' + version
940         tar_name = tar_base + '.tar.gz'
941         ball_prefix = os.path.join (outdir, tar_base)
942         tar_ball = os.path.join (outdir, tar_name)
943
944         dist_files = src_files + txt_files
945         ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
946         map (lambda x: env.Depends (tar_ball, x), ball_files)
947         map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
948                                     'ln $SOURCE $TARGET'), dist_files)
949         tar = env.Command (tar_ball, src_files,
950                            ['rm -f $$(find $TARGET.dir -name .sconsign)',
951                             'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
952         env.Alias ('tar', tar)
953
954         dist_ball = os.path.join (package.release_dir, tar_name)
955         env.Command (dist_ball, tar_ball,
956                      'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
957                      + 'ln $SOURCE $TARGET')
958         env.Depends ('dist', dist_ball)
959         patch_name = os.path.join (outdir, tar_base + '.diff.gz')
960         patch = env.PATCH (patch_name, tar_ball)
961         env.Depends (patch_name, dist_ball)
962         env.Alias ('release', patch)
963
964 #### web
965 if not env['fast']:
966         web_base = os.path.join (outdir, 'web')
967         web_ball = web_base + '.tar.gz'
968         env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
969         web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
970         web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"') + '-or -type l'
971         env['web_path'] = web_path
972         web_list = os.path.join (outdir, 'weblist')
973         # compatible make heritits
974         # fixme: generate in $outdir is cwd/builddir
975         env.Command (web_list,
976                      ## Adding 'doc' dependency is correct, but takes
977                      ## > 5min extra if you have a peder :-)
978                      #'doc',
979                      
980                      '#/VERSION',
981                      ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
982                       'cd $absbuild && $footify $$(find . -name "*.html" -print)',
983                       'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
984                       'cd $absbuild && find Documentation input $web_path \
985                       > $TARGET',
986                       '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
987                       '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
988                       'cd $absbuild && ls *.html >> $TARGET',])
989         env.Command (web_ball, web_list,
990                      ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
991         #env.Alias ('web', web_ball)
992         www_base = os.path.join (outdir, 'www')
993         www_ball = www_base + '.tar.gz'
994         env.Command (www_ball, web_ball,
995                      ['rm -rf $out/tmp',
996                       'mkdir -p $absbuild/$out/tmp',
997                       'tar -C $absbuild/$out/tmp -xzf $SOURCE',
998                       'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); '
999                       + ' do mv $$i $$(dirname $$i)/out-www; done',
1000                       'tar -C $absbuild/$out/tmp -czf $TARGET .'])
1001         env.Alias ('web', www_ball)
1002
1003 #### tags
1004 env.Append (
1005         ETAGSFLAGS = """--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\\1/' \
1006         --regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\\1/'""")
1007 code_ext = ['.cc', '.hh', '.scm', '.tcc',]
1008 env.Command ('TAGS', filter (lambda x: os.path.splitext (x)[1] in code_ext,
1009                              src_files),
1010              'etags $ETAGSFLAGS $SOURCES')
1011
1012 # Note: SConscripts are only needed in directories where something needs
1013 # to be done, building or installing
1014 for d in subdirs:
1015         if os.path.exists (os.path.join (d, 'SConscript')):
1016                 b = os.path.join (env['build'], d, env['out'])
1017                 # Support clean sourcetree build (--srcdir build)
1018                 # and ./out build.
1019                 if os.path.abspath (b) != os.path.abspath (d):
1020                         env.BuildDir (b, d, duplicate = 0)
1021                 SConscript (os.path.join (b, 'SConscript'))
1022