]> git.donarmstrong.com Git - lilypond.git/blob - SConstruct
* SConstruct: Further development.
[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
26
27     lilypond-bin 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 scrdir build, output below DIR
42     scons out=DIR              # write output for alterative config to DIR
43
44 Optional custom.py
45
46 import os
47 out='out-scons'
48 optimising=0
49 debugging=1
50 gui=1
51 os.path.join (os.getcwd (), '=install')
52 prefix=os.path.join (os.environ['HOME'], 'usr', 'pkg', 'lilypond')
53
54 '''
55
56
57 # TODO:
58 #  * add missing dirs:
59 #     - input/mutopia
60 #     - vim
61 #  * more program configure tests (mfont, ...?)
62
63 #  * more fine-grained config.hh -- move lilypondprefix to version.hh?
64 #    - config.hh:   changes after system upgrades, affects all files
65 #    - version.hh:  prefix, version etc?  affects few
66
67 #  * grep FIXME $(find . -name 'S*t')
68
69
70 import re
71 import glob
72 import os
73 import string
74 import sys
75 import stat
76 import shutil
77
78 # duh, we need 0.95.1
79 EnsureSConsVersion (0, 95)
80
81 # SConscripts are only needed in directories where something needs
82 # to be done, building or installing
83 subdirs = ['flower', 'lily', 'mf', 'scm', 'ly',
84            'scripts', 'elisp', 'buildscripts', 'po',
85            'Documentation', 'Documentation/user', 'Documentation/topdocs',
86            'Documentation/bibliography',
87            'input', 'input/regression', 'input/test', 'input/template',
88            'cygwin', 'debian',
89            ]
90
91
92 usage = r'''Usage:
93 scons [KEY=VALUE].. [TARGET|DIR]..
94
95 TARGETS: clean, config, doc, dist, install, mf-essential, po-update,
96          realclean, release, tar, TAGS
97
98 '''
99       
100
101 config_cache = 'config.cache'
102
103 config_vars = (
104         'BASH',
105         'CFLAGS',
106         'CPPPATH',
107         'CXXFLAGS',
108         'DEFINES',
109         'LIBS',
110         'LINKFLAGS',
111         'METAFONT',
112         'PERL',
113         'PYTHON',
114         )
115
116 # Put your favourite stuff in custom.py
117 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
118 opts.Add ('prefix', 'Install prefix', '/usr/')
119 opts.Add ('out', 'Output directory', 'out-scons')
120 opts.Add ('build', 'Build directory', '.')
121 opts.AddOptions (
122         BoolOption ('warnings', 'compile with -Wall and similiar',
123                    1),
124         BoolOption ('debugging', 'compile with debugging symbols',
125                     0),
126         BoolOption ('optimising', 'compile with optimising',
127                     1),
128         BoolOption ('shared', 'build shared libraries',
129                     0),
130         BoolOption ('static', 'build static libraries',
131                     1),
132         BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
133                     1),
134         BoolOption ('verbose', 'run commands with verbose flag',
135                     0),
136         BoolOption ('checksums', 'use checksums instead of timestamps',
137                     1),
138         )
139
140 srcdir = Dir ('.').srcnode ().abspath
141
142 #ugh
143 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
144 import packagepython
145 package = packagepython.Package (srcdir)
146 version = packagepython.version_tuple_to_str (package.version)
147
148 ENV = { 'PATH' : os.environ['PATH'] }
149 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH']:
150         if os.environ.has_key (key):
151                 ENV[key] = os.environ[key]
152
153 env = Environment (
154         ENV = ENV,
155
156         BASH = '/bin/bash',
157         MAKEINFO = 'LANG= makeinfo',
158         PERL = '/usr/bin/perl',
159         PYTHON = '/usr/bin/python',
160         SH = '/bin/sh',
161         
162         ABC2LY_PY = srcdir + '/scripts/abc2ly.py',
163         LILYPOND_BOOK = srcdir + '/scripts/lilypond-book.py',
164         LILYPOND_BOOK_FLAGS = '',
165         LILYPOND_BOOK_FORMAT = 'texi-html',
166         LILYPOND_PY = srcdir + '/scripts/lilypond.py',
167         MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
168         
169         PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
170                                          'usr/pkg/gnome/lib'),
171                            os.path.join (os.environ['HOME'],
172                                          'usr/pkg/pango/lib')],
173         MFMODE = 'ljfour',
174         TEXINFO_PAPERSIZE_OPTION = '-t @afourpaper',
175         TOPLEVEL_VERSION = version,
176         )
177
178 Help (usage + opts.GenerateHelpText (env))
179
180 # Add all config_vars to opts, so that they will be read and saved
181 # together with the other configure options.
182 map (lambda x: opts.AddOptions ((x,)), config_vars)
183 opts.Update (env)
184
185 env['checksums'] = 1
186 if env['checksums']:
187         SetOption ('max_drift', 0)
188
189 absbuild = Dir (env['build']).abspath
190 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
191 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
192
193 CacheDir (os.path.join (outdir, 'build-cache'))
194
195 if env['debugging']:
196         # No need to set $LILYPONDPREFIX to run lily, but cannot install...
197         env['prefix'] = run_prefix
198         
199 prefix = env['prefix']
200 bindir = os.path.join (prefix, 'bin')
201 sharedir = os.path.join (prefix, 'share')
202 libdir = os.path.join (prefix, 'lib')
203 localedir = os.path.join (sharedir, 'locale')
204 sharedir_package = os.path.join (sharedir, package.name)
205 sharedir_package_version = os.path.join (sharedir_package, version)
206 lilypondprefix = sharedir_package_version
207
208 # post-option environment-update
209 env.Append (
210         srcdir = srcdir,
211         
212         bindir = bindir,
213         sharedir = sharedir,
214         lilypond_datadir = sharedir_package,
215         localedir = localedir,
216         local_lilypond_datadir = sharedir_package_version,
217         lilypondprefix = lilypondprefix,
218         sharedir_package = sharedir_package,
219         sharedir_package_version = sharedir_package_version,
220         )
221
222 if env['debugging']:
223         env.Append (CFLAGS = '-g')
224         env.Append (CXXFLAGS = '-g')
225 if env['optimising']:
226         env.Append (CFLAGS = '-O2')
227         env.Append (CXXFLAGS = ['-O2', '-DSTRING_UTILS_INLINED'])
228 if env['warnings']:
229         env.Append (CFLAGS = ['-W', '-Wall'])
230         # CXXFLAGS = $CFLAGS ...
231         env.Append (CXXFLAGS = ['-W', '-Wall', '-Wconversion'])
232 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
233 if env['verbose']:
234         env['__verbose'] = ' --verbose'
235         env['set__x'] = 'set -x;'
236
237 config_hh = os.path.join (outdir, 'config.hh')
238 version_hh = os.path.join (outdir, 'version.hh')
239
240 env.Alias ('config', config_cache)
241
242
243 ## Explicit target and dependencies
244
245 if 'clean' in COMMAND_LINE_TARGETS:
246         # ugh: prevent reconfigure instead of clean
247         os.system ('touch %s' % config_cache)
248         
249         command = sys.argv[0] + ' -c .'
250         sys.stdout.write ('Running %s ... ' % command)
251         sys.stdout.write ('\n')
252         s = os.system (command)
253         if os.path.exists (config_cache):
254                 os.unlink (config_cache)
255         Exit (s)
256
257 if 'realclean' in COMMAND_LINE_TARGETS:
258         command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
259         sys.stdout.write ('Running %s ... ' % command)
260         sys.stdout.write ('\n')
261         s = os.system (command)
262         if os.path.exists (config_cache):
263                 os.unlink (config_cache)
264         Exit (s)
265         
266 # Without target arguments, build lily only
267 if not COMMAND_LINE_TARGETS:
268         env.Default ('lily')
269 env.Alias ('all', '.')
270 env.Alias ('doc',
271            ['Documentation',
272             'Documentation/user',
273             'Documentation/topdocs'])
274
275 env.Depends ('doc', ['lily', 'mf'])
276 env.Depends ('input', ['lily', 'mf'])
277
278
279 def list_sort (lst):
280         sorted = lst
281         sorted.sort ()
282         return sorted
283
284 def configure (target, source, env):
285         vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
286         def get_version (program):
287                 command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
288                 pipe = os.popen (command)
289                 output = pipe.read ()
290                 if pipe.close ():
291                         return None
292                 v = re.sub (vre, '\\1', output)
293                 return string.split (v, '.')
294
295         def test_program (lst, program, minimal, description, package):
296                 sys.stdout.write ('Checking %s version... ' % program)
297                 actual = get_version (program)
298                 if not actual:
299                         print 'not found'
300                         lst.append ((description, package, minimal, program,
301                                      'not installed'))
302                         return
303                 sys.stdout.write (string.join (actual, '.'))
304                 sys.stdout.write ('\n')
305                 if actual < string.split (minimal, '.'):
306                         lst.append ((description, package, minimal, program,
307                                      string.join (actual, '.')))
308
309         required = []
310         test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
311         test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
312         test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
313         test_program (required, 'guile-config', '1.6', 'GUILE development',
314                         'libguile-dev or guile-devel')
315         # Do not use bison 1.50 and 1.75.
316         test_program (required, 'bison', '1.25', 'Bison -- parser generator',
317                         'bison')
318         test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
319
320
321         optional = []
322         test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
323         test_program (optional, 'guile', '1.6', 'GUILE scheme',
324                         'libguile-dev or guile-devel')
325         test_program (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
326                         'mftrace')
327         test_program (optional, 'perl', '4.0',
328                         'Perl practical efficient readonly language', 'perl')
329         #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
330
331         def CheckYYCurrentBuffer (context):
332                 context.Message ('Checking for yy_current_buffer... ')
333                 ret = conf.TryCompile ("""using namespace std;
334                 #include <FlexLexer.h>
335                 class yy_flex_lexer: public yyFlexLexer
336                 {
337                 public:
338                 yy_flex_lexer ()
339                 {
340                 yy_current_buffer = 0;
341                 }
342                 };""", '.cc')
343                 context.Result (ret)
344                 return ret
345
346         conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
347                                                 : CheckYYCurrentBuffer })
348
349         defines = {
350            'DIRSEP' : "'/'",
351            'PATHSEP' : "':'",
352            'TOPLEVEL_VERSION' : '"' + version + '"',
353            'PACKAGE': '"' + package.name + '"',
354            'DATADIR' : '"' + sharedir + '"',
355            'LILYPOND_DATADIR' : '"' + sharedir_package + '"',
356            'LOCAL_LILYPOND_DATADIR' : '"' + sharedir_package_version + '"',
357            'LOCALEDIR' : '"' + localedir + '"',
358         }
359         conf.env.Append (DEFINES = defines)
360
361         command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
362         PYTHON_INCLUDE = os.popen (command).read ()
363         env.Append (CPPPATH = PYTHON_INCLUDE)
364
365         headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
366         for i in headers:
367                 if conf.CheckCHeader (i):
368                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
369                         conf.env['DEFINES'][key] = 1
370
371         ccheaders = ('sstream',)
372         for i in ccheaders:
373                 if conf.CheckCXXHeader (i):
374                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
375                         conf.env['DEFINES'][key] = 1
376
377         functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
378         for i in functions:
379                 if 0 or conf.CheckFunc (i):
380                         key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
381                         conf.env['DEFINES'][key] = 1
382
383         if conf.CheckYYCurrentBuffer ():
384                 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
385
386         if conf.CheckLib ('dl'):
387                 pass
388
389         if conf.CheckLib ('kpathsea'):
390                 conf.env['DEFINES']['KPATHSEA'] = 1
391
392         # huh? 
393         if conf.CheckLib ('kpathsea', 'kpse_find_file'):
394                 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
395         if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
396                 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
397
398         #this could happen after flower...
399         env.ParseConfig ('guile-config compile')
400
401         #this could happen only for compiling pango-*
402         if env['gui']:
403                 env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
404                 env.ParseConfig ('pkg-config --cflags --libs pango')
405                 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
406                         conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
407
408                 if conf.CheckLib ('pango-1.0',
409                                   'pango_fc_font_map_add_decoder_find_func'):
410                         conf.env['DEFINES']['HAVE_PANGO_CVS'] = '1'
411                         conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
412
413         if required:
414                 print
415                 print '********************************'
416                 print 'Please install required packages'
417                 for i in required:
418                         print '%s:      %s-%s or newer (found: %s %s)' % i
419                 Exit (1)
420
421         if optional:
422                 print
423                 print '*************************************'
424                 print 'Consider installing optional packages'
425                 for i in optional:
426                         print '%s:      %s-%s or newer (found: %s %s)' % i
427
428         return conf.Finish ()
429
430 def config_header (target, source, env):
431         config = open (str (target[0]), 'w')
432         for i in list_sort (env['DEFINES'].keys ()):
433                 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
434         config.close ()
435 env.Command (config_hh, config_cache, config_header)
436
437 # hmm?
438 def xuniquify (lst):
439         n = []
440         for i in lst:
441                 if not i in n:
442                         n.append (i)
443         lst = n
444         return lst
445
446 def uniquify (lst):
447         d = {}
448         n = len (lst)
449         i = 0
450         while i < n:
451                 if not d.has_key (lst[i]):
452                         d[lst[i]] = 1
453                         i += 1
454                 else:
455                         del lst[i]
456                         n -= 1
457         return lst
458
459
460 def save_config_cache (env):
461         ## FIXME: Is this smart, using option cache for saving
462         ## config.cache?  I cannot seem to find the official method.
463         for i in config_vars:
464                 if env.has_key (i) and type (env[i]) == type ([]):
465                         env[i] = uniquify (env[i])
466         opts.Save (config_cache, env)
467
468         ## FIXME: Something changes in the ENVironment that triggers
469         ## rebuild of everything if we continue after configuring.
470         ## Maybe there's a variable missing from config_vars?
471                 
472         ## How to print and debug the reasons scons has for rebuilding
473         ## a target (make --debug)?
474
475         ## heet van de naald:
476         ## - Add a --debug=explain option that reports the reason(s) why SCons
477
478         if 'config' in COMMAND_LINE_TARGETS:
479                 sys.stdout.write ('\n')
480                 sys.stdout.write ('LilyPond configured')
481                 sys.stdout.write ('\n')
482                 sys.stdout.write ('now run')
483                 sys.stdout.write ('\n')
484                 sys.stdout.write ('    scons [TARGET|DIR]...')
485                 sys.stdout.write ('\n')
486                 Exit (0)
487         elif 1: #not env['checksums']:
488                 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
489                 sys.stdout.write ('Running %s ... ' % command)
490                 sys.stdout.write ('\n')
491                 s = os.system (command)
492                 Exit (s)
493
494
495 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
496         os.unlink (config_cache)
497 # WTF?
498 # scons: *** Calling Configure from Builders is not supported.
499 # env.Command (config_cache, None, configure)
500 if not os.path.exists (config_cache) \
501    or (os.stat ('SConstruct')[stat.ST_MTIME]
502        > os.stat (config_cache)[stat.ST_MTIME]):
503         env = configure (None, None, env)
504         save_config_cache (env)
505
506 env.Command (version_hh, '#/VERSION',
507              '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
508
509 # post-config environment update
510 env.Append (
511         absbuild = absbuild,
512         run_prefix = run_prefix,
513         LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
514
515         LIBPATH = [os.path.join (absbuild, 'flower', env['out']),],
516         CPPPATH = [outdir, '#',],
517         LILYPOND_BIN = os.path.join (absbuild, 'lily', env['out'],
518                                      'lilypond-bin'),
519         LILYPOND_BOOK_PATH = ['.', '#/input', '#/input/regression',
520                               '#/input/test', '#/input/tutorial',
521                               os.path.join (absbuild, 'mf', env['out']),
522                               '#/Documentation/user',
523                               os.path.join (absbuild, 'Documentation',
524                                             env['out']),
525                               os.path.join (absbuild, 'Documentation/user',
526                                             env['out']),
527                               ],
528         MAKEINFO_PATH = ['.', '#/Documentation/user',
529                          os.path.join (absbuild, 'Documentation/user',
530                                        env['out'])],
531         )
532
533 Export ('env')
534 SConscript ('buildscripts/builder.py')
535
536
537 def symlink_tree (target, source, env):
538         def mkdirs (dir):
539                 def mkdir (dir):
540                         if not dir:
541                                 os.chdir (os.sep)
542                                 return
543                         if not os.path.isdir (dir):
544                                 if os.path.exists (dir):
545                                         os.unlink (dir)
546                                 os.mkdir (dir)
547                         os.chdir (dir)
548                 map (mkdir, string.split (dir, os.sep))
549         def symlink (src, dst):
550                 os.chdir (absbuild)
551                 dir = os.path.dirname (dst)
552                 mkdirs (dir)
553                 if src[0] == '#':
554                         frm = os.path.join (srcdir, src[1:])
555                 else:
556                         depth = len (string.split (dir, '/'))
557                         if src.find ('@') > -1:
558                                 frm = os.path.join ('../' * depth,
559                                                     string.replace (src, '@',
560                                                                     env['out']))
561                         else:
562                                 frm = os.path.join ('../' * depth, src,
563                                                     env['out'])
564                         if src[-1] == '/':
565                                 frm = os.path.join (frm, os.path.basename (dst))
566                 if env['verbose']:
567                         print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
568                 os.symlink (frm, os.path.basename (dst))
569         shutil.rmtree (run_prefix)
570         prefix = os.path.join (env['out'], 'usr')
571         map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
572              # ^# := source dir
573              # @  := out
574              # /$ := add dst file_name
575              (('python',     'lib/lilypond/python'),
576               ('lily/',      'bin/lilypond-bin'),
577               ('scripts/',   'bin/lilypond'),
578               ('scripts/',   'bin/lilypond-book'),
579               ('#mf',        'share/lilypond/fonts/mf'),
580               ('mf',         'share/lilypond/fonts/afm'),
581               ('mf',         'share/lilypond/fonts/tfm'),
582               ('mf',         'share/lilypond/fonts/type1'),
583               ('#tex',       'share/lilypond/tex/source'),
584               ('mf',         'share/lilypond/tex/generate'),
585               ('#ly',        'share/lilypond/ly'),
586               ('#scm',       'share/lilypond/scm'),
587               ('#ps',        'share/lilypond/ps'),
588               ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
589               ('elisp',      'share/lilypond/elisp')))
590         os.chdir (srcdir)
591
592 if env['debugging']:
593         stamp = os.path.join (run_prefix, 'stamp')
594         env.Command (stamp, 'SConstruct', [symlink_tree, 'touch $TARGET'])
595         env.Depends ('lily', stamp)
596         
597 #### dist, tar
598 def plus (a, b):
599         a + b
600
601 def cvs_entry_is_dir (line):
602         return line[0] == 'D' and line[-2] == '/'
603
604 def cvs_entry_is_file (line):
605         return line[0] == '/' and line[-2] == '/'
606
607 def cvs_dirs (dir):
608         ENTRIES = os.path.join (dir, 'CVS/Entries')
609         if not os.path.exists (ENTRIES):
610                 return []
611         entries = open (ENTRIES).readlines ()
612         dir_entries = filter (cvs_entry_is_dir, entries)
613         dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
614                     dir_entries)
615         return dirs + map (cvs_dirs, dirs)
616
617 def cvs_files (dir):
618         ENTRIES = os.path.join (dir, 'CVS/Entries')
619         entries = open (ENTRIES).readlines ()
620         file_entries = filter (cvs_entry_is_file, entries)
621         files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
622         return map (lambda x: os.path.join (dir, x), files)
623
624 #subdirs = reduce (lambda x, y: x + y, cvs_dirs ('.'))
625 #print `subdirs`
626 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
627 foo = map (lambda x: env.TXT (x + '.txt',
628                               os.path.join ('Documentation/topdocs', x)),
629            readme_files)
630 txt_files = map (lambda x: x + '.txt', readme_files)
631 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
632 tar_base = package.name + '-' + version
633 tar_name = tar_base + '.tar.gz'
634 ball_prefix = os.path.join (outdir, tar_base)
635 tar_ball = os.path.join (outdir, tar_name)
636
637 dist_files = src_files + txt_files
638 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
639 map (lambda x: env.Depends (tar_ball, x), ball_files)
640 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
641                             'ln $SOURCE $TARGET'), dist_files)
642 tar = env.Command (tar_ball, src_files,
643                    'tar czf $TARGET -C $TARGET.dir %s' % tar_base)
644 env.Alias ('tar', tar)
645
646 dist_ball = os.path.join (package.release_dir, tar_name)
647 env.Command (dist_ball, tar_ball,
648              'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
649              + 'ln $SOURCE $TARGET')
650 env.Depends ('dist', dist_ball)
651 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
652 patch = env.PATCH (patch_name, tar_ball)
653 env.Depends (patch_name, dist_ball)
654 env.Alias ('release', patch)
655
656 env.Append (
657         ETAGSFLAGS = ["""--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\1/'""",
658                       """--regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\1/'"""])
659 # filter-out some files?
660 env.Command ('TAGS', src_files, 'etags $ETAGSFLAGS $SOURCES')
661
662 for d in subdirs:
663         if os.path.exists (os.path.join (d, 'SConscript')):
664                 b = os.path.join (env['build'], d, env['out'])
665                 # Support clean sourcetree build (--srcdir build)
666                 # and ./out build.
667                 if os.path.abspath (b) != os.path.abspath (d):
668                         env.BuildDir (b, d, duplicate = 0)
669                 SConscript (os.path.join (b, 'SConscript'))