4 Experimental scons (www.scons.org) building.
10 build from source directory ./TARGET (not recursive)
14 scons [config] # configure
19 run=$(pwd)/out-scons/usr
20 export LOCALE=$run/share/locale
21 export TEXMF='{'$run/share/lilypond,$(kpsexpand '$TEXMF')'}'
24 #optionally, if you do not use custom.py below
25 #export LILYPONDPREFIX=$run/share/lilypond
27 lilypond-bin input/simple
30 scons mf-essential # build minimal mf stuff
32 scons doc # build web doc
33 scons config # reconfigure
34 scons install # install
38 scons / # build *everything* (including installation)
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
56 os.path.join (os.getcwd (), '=install')
57 prefix=os.path.join (os.environ['HOME'], 'usr', 'pkg', 'lilypond')
65 # * more program configure tests (mfont, ...?)
69 # * split doc target: doc input examples mutopia?
71 # * grep FIXME $(find . -name 'S*t')
83 EnsureSConsVersion (0, 95)
86 scons [KEY=VALUE].. [TARGET|DIR]..
88 TARGETS: clean, config, doc, dist, install, mf-essential, po-update,
89 realclean, release, tar, TAGS
94 config_cache = 'scons.cache'
110 # Put your favourite stuff in custom.py
111 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
112 opts.Add ('prefix', 'Install prefix', '/usr/')
113 opts.Add ('out', 'Output directory', 'out-scons')
114 opts.Add ('build', 'Build directory', '.')
115 opts.Add ('DESTDIR', 'DESTDIR prepended to prefix', '')
117 BoolOption ('warnings', 'compile with -Wall and similiar',
119 BoolOption ('debugging', 'compile with debugging symbols',
121 BoolOption ('optimising', 'compile with optimising',
123 BoolOption ('shared', 'build shared libraries',
125 BoolOption ('static', 'build static libraries',
127 BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
129 BoolOption ('verbose', 'run commands with verbose flag',
131 BoolOption ('checksums', 'use checksums instead of timestamps',
135 srcdir = Dir ('.').srcnode ().abspath
137 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
139 package = packagepython.Package (srcdir)
140 version = packagepython.version_tuple_to_str (package.version)
142 ENV = { 'PATH' : os.environ['PATH'] }
143 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH']:
144 if os.environ.has_key (key):
145 ENV[key] = os.environ[key]
151 PERL = '/usr/bin/perl',
152 PYTHON = '/usr/bin/python',
155 MAKEINFO = 'LANG= makeinfo',
156 MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
158 PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
159 'usr/pkg/gnome/lib'),
160 os.path.join (os.environ['HOME'],
161 'usr/pkg/pango/lib')],
164 TOPLEVEL_VERSION = version,
167 # Add all config_vars to opts, so that they will be read and saved
168 # together with the other configure options.
169 map (lambda x: opts.AddOptions ((x,)), config_vars)
171 Help (usage + opts.GenerateHelpText (env))
175 # Using content checksums prevents rebuilds after [re]configure if
176 # config.hh has not changed.
178 SetOption ('max_drift', 0)
179 TargetSignatures ("content")
181 absbuild = Dir (env['build']).abspath
182 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
183 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
186 config_hh = os.path.join (outdir, 'config.hh')
187 version_hh = os.path.join (outdir, 'version.hh')
189 env.Alias ('config', config_cache)
191 cachedir = os.path.join (outdir, 'build-cache')
193 if not os.path.exists(cachedir):
194 os.makedirs(cachedir)
196 CacheDir (os.path.join (outdir, 'build-cache'))
198 # No need to set $LILYPONDPREFIX to run lily, but cannot install...
199 if env['debugging'] and not 'install' in COMMAND_LINE_TARGETS:
200 env['prefix'] = run_prefix
202 prefix = env['prefix']
203 bindir = os.path.join (prefix, 'bin')
204 sharedir = os.path.join (prefix, 'share')
205 libdir = os.path.join (prefix, 'lib')
206 localedir = os.path.join (sharedir, 'locale')
207 sharedir_doc_package = os.path.join (sharedir, 'doc', package.name)
208 sharedir_package = os.path.join (sharedir, package.name)
209 sharedir_package_version = os.path.join (sharedir_package, version)
210 lilypondprefix = sharedir_package_version
225 def configure (target, source, env):
226 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
227 def get_version (program):
228 command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
229 pipe = os.popen (command)
230 output = pipe.read ()
233 v = re.sub (vre, '\\1', output)
234 return string.split (v, '.')
236 def test_program (lst, program, minimal, description, package):
237 sys.stdout.write ('Checking %s version... ' % program)
238 actual = get_version (program)
241 lst.append ((description, package, minimal, program,
244 sys.stdout.write (string.join (actual, '.'))
245 sys.stdout.write ('\n')
246 if actual < string.split (minimal, '.'):
247 lst.append ((description, package, minimal, program,
248 string.join (actual, '.')))
250 for i in ['bash', 'perl', 'python', 'sh']:
251 sys.stdout.write ('Checking for %s... ' % i)
253 key = string.upper (i)
258 sys.stdout.write ('not found: %s (using: %s)' \
261 sys.stdout.write ('\n')
264 test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
265 test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
266 test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
267 test_program (required, 'guile-config', '1.6', 'GUILE development',
268 'libguile-dev or guile-devel')
269 # Do not use bison 1.50 and 1.75.
270 test_program (required, 'bison', '1.25', 'Bison -- parser generator',
272 test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
276 test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
277 test_program (optional, 'guile', '1.6', 'GUILE scheme',
278 'libguile-dev or guile-devel')
279 test_program (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
281 test_program (optional, 'perl', '4.0',
282 'Perl practical efficient readonly language', 'perl')
283 #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
285 def CheckYYCurrentBuffer (context):
286 context.Message ('Checking for yy_current_buffer... ')
287 ret = conf.TryCompile ("""using namespace std;
288 #include <FlexLexer.h>
289 class yy_flex_lexer: public yyFlexLexer
294 yy_current_buffer = 0;
300 conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
301 : CheckYYCurrentBuffer })
304 'DIRSEP' : "'%s'" % os.sep,
305 'PATHSEP' : "'%s'" % os.pathsep,
306 'TOPLEVEL_VERSION' : '"' + version + '"',
307 'PACKAGE': '"' + package.name + '"',
308 'DATADIR' : '"' + sharedir + '"',
309 'LILYPOND_DATADIR' : '"' + sharedir_package + '"',
310 'LOCAL_LILYPOND_DATADIR' : '"' + sharedir_package_version + '"',
311 'LOCALEDIR' : '"' + localedir + '"',
313 conf.env.Append (DEFINES = defines)
315 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
316 PYTHON_INCLUDE = os.popen (command).read ()
317 env.Append (CPPPATH = PYTHON_INCLUDE)
319 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
321 if conf.CheckCHeader (i):
322 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
323 conf.env['DEFINES'][key] = 1
325 ccheaders = ('sstream',)
327 if conf.CheckCXXHeader (i):
328 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
329 conf.env['DEFINES'][key] = 1
331 functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
333 if 0 or conf.CheckFunc (i):
334 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
335 conf.env['DEFINES'][key] = 1
337 if conf.CheckYYCurrentBuffer ():
338 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
340 if conf.CheckLib ('dl'):
343 if conf.CheckLib ('kpathsea'):
344 conf.env['DEFINES']['KPATHSEA'] = 1
347 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
348 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
349 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
350 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
352 #this could happen after flower...
353 env.ParseConfig ('guile-config compile')
355 #this could happen only for compiling pango-*
357 env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
358 env.ParseConfig ('pkg-config --cflags --libs pango')
359 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
360 conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
362 if conf.CheckLib ('pango-1.0',
363 'pango_fc_font_map_add_decoder_find_func'):
364 conf.env['DEFINES']['HAVE_PANGO_CVS'] = '1'
365 conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
369 print '********************************'
370 print 'Please install required packages'
372 print '%s: %s-%s or newer (found: %s %s)' % i
377 print '*************************************'
378 print 'Consider installing optional packages'
380 print '%s: %s-%s or newer (found: %s %s)' % i
382 return conf.Finish ()
384 def config_header (target, source, env):
385 config = open (str (target[0]), 'w')
386 for i in list_sort (env['DEFINES'].keys ()):
387 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
389 env.Command (config_hh, config_cache, config_header)
405 if not d.has_key (lst[i]):
413 def uniquify_config_vars (env):
414 for i in config_vars:
415 if env.has_key (i) and type (env[i]) == type ([]):
416 env[i] = uniquify (env[i])
418 def save_config_cache (env):
419 ## FIXME: Is this smart, using option cache for saving
420 ## config.cache? I cannot seem to find the official method.
421 uniquify_config_vars (env)
422 opts.Save (config_cache, env)
424 if 'config' in COMMAND_LINE_TARGETS:
425 sys.stdout.write ('\n')
426 sys.stdout.write ('LilyPond configured')
427 sys.stdout.write ('\n')
428 sys.stdout.write ('now run')
429 sys.stdout.write ('\n')
430 sys.stdout.write (' scons [TARGET|DIR]...')
431 sys.stdout.write ('\n')
433 elif not env['checksums']:
434 # When using timestams, config.hh is NEW. The next
435 # build triggers recompilation of everything. Exiting
436 # here makes SCons use the actual timestamp for config.hh
437 # and prevents recompiling everything the next run.
438 command = sys.argv[0] + ' ' + string.join (COMMAND_LINE_TARGETS)
439 sys.stdout.write ('Running %s ... ' % command)
440 sys.stdout.write ('\n')
441 s = os.system (command)
445 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
446 os.unlink (config_cache)
448 # scons: *** Calling Configure from Builders is not supported.
449 # env.Command (config_cache, None, configure)
450 if not os.path.exists (config_cache) \
451 or (os.stat ('SConstruct')[stat.ST_MTIME]
452 > os.stat (config_cache)[stat.ST_MTIME]):
453 env = configure (None, None, env)
454 save_config_cache (env)
455 elif env['checksums']:
456 # just save everything
457 save_config_cache (env)
459 #urg how does #/ subst work?
461 SConscript ('buildscripts/builder.py')
463 env.PrependENVPath ('PATH',
464 os.path.join (env['absbuild'], env['out'], 'usr/bin'))
466 if os.environ.has_key ('TEXMF'):
467 env.Append (ENV = {'TEXMF' : os.environ['TEXMF']})
469 'TEXMF' : '{$LILYPONDPREFIX,' \
470 + os.popen ('kpsexpand \$TEXMF').read ()[:-1] + '}',
471 'LILYPONDPREFIX' : os.path.join (run_prefix, 'share/lilypond'),
474 BUILD_ABC2LY = '${set__x}$PYTHON $srcdir/scripts/abc2ly.py'
475 BUILD_LILYPOND = '${set__x}$PYTHON $srcdir/scripts/lilypond.py${__verbose}'
476 BUILD_LILYPOND_BIN = '$absbuild/$out/lilypond-bin ${__verbose}'
477 BUILD_LILYPOND_BOOK = '$PYTHON $srcdir/scripts/lilypond-book.py --verbose'
480 # post-option environment-update
484 lilypond_datadir = sharedir_package,
485 localedir = localedir,
486 local_lilypond_datadir = sharedir_package_version,
487 lilypondprefix = lilypondprefix,
488 sharedir_package = sharedir_package,
489 sharedir_doc_package = sharedir_doc_package,
490 sharedir_package_version = sharedir_package_version,
492 LILYPOND = BUILD_LILYPOND,
493 ABC2LY = BUILD_ABC2LY,
494 LILYPOND_BOOK = BUILD_LILYPOND_BOOK,
495 LILYPOND_BOOK_FORMAT = 'texi-html',
496 MAKEINFO_FLAGS = '--css-include=$srcdir/Documentation/texinfo.css',
498 TEXI2DVI_PAPERSIZE = '@afourpaper',
499 TEXI2DVI_FLAGS = [ '-t $TEXI2DVI_PAPERSIZE'],
500 DVIPS_PAPERSIZE = 'a4',
501 DVIPS_FLAGS = ['-t $DVIPS_PAPERSIZE',
503 '-u+ec-mftrace.map'],
504 PSPDF_FLAGS = ['-sPAPERSIZE=$DVIPS_PAPERSIZE'],
508 env.Append (CCFLAGS = ['-g', '-pipe'])
509 if env['optimising']:
510 env.Append (CCFLAGS = '-O2')
511 env.Append (CXXFLAGS = ['-DSTRING_UTILS_INLINED'])
513 env.Append (CCFLAGS = ['-W', '-Wall'])
514 env.Append (CXXFLAGS = ['-Wconversion'])
517 env.Append (LINKFLAGS = ['-Wl,--export-dynamic'])
520 env['__verbose'] = ' --verbose'
521 env['set__x'] = 'set -x;'
524 ## Explicit target and dependencies
526 if 'clean' in COMMAND_LINE_TARGETS:
527 # ugh: prevent reconfigure instead of clean
528 os.system ('touch %s' % config_cache)
530 command = sys.argv[0] + ' -c .'
531 sys.stdout.write ('Running %s ... ' % command)
532 sys.stdout.write ('\n')
533 s = os.system (command)
534 if os.path.exists (config_cache):
535 os.unlink (config_cache)
538 if 'realclean' in COMMAND_LINE_TARGETS:
539 command = 'rm -rf $(find . -name "out-scons" -o -name ".scon*")'
540 sys.stdout.write ('Running %s ... ' % command)
541 sys.stdout.write ('\n')
542 s = os.system (command)
543 if os.path.exists (config_cache):
544 os.unlink (config_cache)
547 # Declare SConscript phonies
548 env.Alias ('minimal', config_cache)
549 env.Alias ('mf-essential', config_cache)
551 env.Alias ('minimal', ['lily', 'mf-essential'])
552 env.Alias ('all', ['minimal', 'mf', '.'])
553 # Do we want the doc/web separation?
556 'Documentation/user',
557 'Documentation/topdocs',
558 'Documentation/bibliography',
561 # Without target arguments, do minimal build
562 if not COMMAND_LINE_TARGETS:
563 env.Default (['minimal'])
565 # GNU Make rerouting compat:
566 env.Alias ('web', 'doc')
569 env.Command (version_hh, '#/VERSION',
570 '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
572 # post-config environment update
574 run_prefix = run_prefix,
575 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
577 LIBPATH = [os.path.join (absbuild, 'flower', env['out']),],
578 ##CPPPATH = [outdir, '#',], # do not read auto*'s header
579 CPPPATH = [outdir, ],
580 LILYPOND_PATH = ['.', '$srcdir/input',
581 '$srcdir/input/regression',
582 '$srcdir/input/test',
583 '$srcdir/input/tutorial',
584 '$srcdir/Documentation/user',
586 # os.path.join (absbuild, 'Documentation',
588 # os.path.join (absbuild, 'Documentation/user',
591 MAKEINFO_PATH = ['.', '$srcdir/Documentation/user',
592 '$absbuild/Documentation/user/$out'],
595 def symlink_tree (target, source, env):
601 if not os.path.isdir (dir):
602 if os.path.exists (dir):
606 map (mkdir, string.split (dir, os.sep))
607 def symlink (src, dst):
609 dir = os.path.dirname (dst)
612 frm = os.path.join (srcdir, src[1:])
614 depth = len (string.split (dir, '/'))
615 if src.find ('@') > -1:
616 frm = os.path.join ('../' * depth,
617 string.replace (src, '@',
620 frm = os.path.join ('../' * depth, src,
623 frm = os.path.join (frm, os.path.basename (dst))
625 print 'ln -s %s -> %s' % (frm, os.path.basename (dst))
626 os.symlink (frm, os.path.basename (dst))
627 shutil.rmtree (run_prefix)
628 prefix = os.path.join (env['out'], 'usr')
629 map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
632 # /$ := add dst file_name
633 (('python', 'lib/lilypond/python'),
634 ('lily/', 'bin/lilypond-bin'),
635 ('scripts/', 'bin/lilypond'),
636 ('scripts/', 'bin/lilypond-book'),
637 ('mf', 'share/lilypond/dvips'),
638 ('#ps', 'share/lilypond/tex/music-drawing-routines.ps'),
639 ('mf', 'share/lilypond/afm'),
640 ('mf', 'share/lilypond/tfm'),
641 ('#mf', 'share/lilypond/fonts/mf'),
642 ('mf', 'share/lilypond/fonts/afm'),
643 ('mf', 'share/lilypond/fonts/tfm'),
644 ('mf', 'share/lilypond/fonts/type1'),
645 ('#tex', 'share/lilypond/tex/source'),
646 ('mf', 'share/lilypond/tex/generate'),
647 ('#ly', 'share/lilypond/ly'),
648 ('#scm', 'share/lilypond/scm'),
649 ('#ps', 'share/lilypond/ps'),
650 ('po/@/nl.mo', 'share/locale/nl/LC_MESSAGES/lilypond.mo'),
651 ('elisp', 'share/lilypond/elisp')))
655 stamp = os.path.join (run_prefix, 'stamp')
656 env.Command (stamp, 'SConstruct', [symlink_tree, 'touch $TARGET'])
657 env.Depends ('lily', stamp)
663 def cvs_entry_is_dir (line):
664 return line[0] == 'D' and line[-2] == '/'
666 def cvs_entry_is_file (line):
667 return line[0] == '/' and line[-2] == '/'
670 ENTRIES = os.path.join (dir, 'CVS/Entries')
671 if not os.path.exists (ENTRIES):
673 entries = open (ENTRIES).readlines ()
674 dir_entries = filter (cvs_entry_is_dir, entries)
675 dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
677 return dirs + map (cvs_dirs, dirs)
680 ENTRIES = os.path.join (dir, 'CVS/Entries')
681 entries = open (ENTRIES).readlines ()
682 file_entries = filter (cvs_entry_is_file, entries)
683 files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
684 return map (lambda x: os.path.join (dir, x), files)
686 def flatten (tree, lst):
687 if type (tree) == type ([]):
689 if type (i) == type ([]):
695 subdirs = flatten (cvs_dirs ('.'), [])
696 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
697 foo = map (lambda x: env.TXT (x + '.txt',
698 os.path.join ('Documentation/topdocs', x)),
700 txt_files = map (lambda x: x + '.txt', readme_files)
701 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
702 tar_base = package.name + '-' + version
703 tar_name = tar_base + '.tar.gz'
704 ball_prefix = os.path.join (outdir, tar_base)
705 tar_ball = os.path.join (outdir, tar_name)
707 dist_files = src_files + txt_files
708 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
709 map (lambda x: env.Depends (tar_ball, x), ball_files)
710 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
711 'ln $SOURCE $TARGET'), dist_files)
712 tar = env.Command (tar_ball, src_files,
713 ['rm -f $$(find $TARGET.dir -name .sconsign)',
714 'tar czf $TARGET -C $TARGET.dir %s' % tar_base,])
715 env.Alias ('tar', tar)
717 dist_ball = os.path.join (package.release_dir, tar_name)
718 env.Command (dist_ball, tar_ball,
719 'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
720 + 'ln $SOURCE $TARGET')
721 env.Depends ('dist', dist_ball)
722 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
723 patch = env.PATCH (patch_name, tar_ball)
724 env.Depends (patch_name, dist_ball)
725 env.Alias ('release', patch)
728 web_base = os.path.join (outdir, 'web')
729 web_ball = web_base + '.tar.gz'
730 env['footify'] = 'MAILADDRESS=bug-lilypond@gnu.org $PYTHON stepmake/bin/add-html-footer.py --name=lilypond --version=$TOPLEVEL_VERSION'
731 web_ext = ['.html', '.ly', '.midi', '.pdf', '.png', '.ps.gz', '.txt',]
732 web_path = '-path "*/$out/*"' + string.join (web_ext, ' -or -path "*/$out/*"')
733 env['web_path'] = web_path
734 web_list = os.path.join (outdir, 'weblist')
735 # compatible make heritits
736 # fixme: generate in $outdir is cwd/builddir
737 env.Command (web_list,
738 ## this is correct, but takes > 5min if you have a peder :-)
741 ['$PYTHON buildscripts/mutopia-index.py -o examples.html ./',
742 'cd $absbuild && $footify $$(find . -name "*.html" -print)',
743 'cd $absbuild && rm -f $$(find . -name "*.html~" -print)',
744 'cd $absbuild && find Documentation input $web_path \
746 '''echo '<META HTTP-EQUIV="refresh" content="0;URL=Documentation/out-www/index.html">' > $absbuild/index.html''',
747 '''echo '<html><body>Redirecting to the documentation index...</body></html>' >> $absbuild/index.html''',
748 'cd $absbuild && ls *.html >> $TARGET',])
749 env.Command (web_ball, web_list,
750 ['cat $SOURCE | tar -C $absbuild -czf $TARGET -T -',])
751 #env.Alias ('web', web_ball)
752 www_base = os.path.join (outdir, 'www')
753 www_ball = www_base + '.tar.gz'
754 env.Command (www_ball, web_ball,
756 'mkdir -p $absbuild/$out/tmp',
757 'tar -C $absbuild/$out/tmp -xzf $SOURCE',
758 'cd $absbuild/$out/tmp && for i in $$(find . -name "$out"); do mv $$i $$(dirname $$i)/out-www; done',
759 'tar -C $absbuild/$out/tmp -czf $TARGET .'])
760 env.Alias ('web', www_ball)
764 ETAGSFLAGS = ["""--regex='{c++}/^LY_DEFINE *(\([^,]+\)/\1/'""",
765 """--regex='{c++}/^LY_DEFINE *([^"]*"\([^"]+\)"/\1/'"""])
766 # filter-out some files?
767 env.Command ('TAGS', src_files, 'etags $ETAGSFLAGS $SOURCES')
770 # Note: SConscripts are only needed in directories where something needs
771 # to be done, building or installing
773 if os.path.exists (os.path.join (d, 'SConscript')):
774 b = os.path.join (env['build'], d, env['out'])
775 # Support clean sourcetree build (--srcdir build)
777 if os.path.abspath (b) != os.path.abspath (d):
778 env.BuildDir (b, d, duplicate = 0)
779 SConscript (os.path.join (b, 'SConscript'))