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