root/branches/libffado-2.0/SConstruct

Revision 1220, 19.5 kB (checked in by ppalmers, 13 years ago)

beta5 version increase

Line 
1 #! /usr/bin/python
2 #
3 # Copyright (C) 2007-2008 Arnold Krille
4 # Copyright (C) 2007-2008 Pieter Palmers
5 # Copyright (C) 2008 Jonathan Woithe
6 #
7 # This file is part of FFADO
8 # FFADO = Free Firewire (pro-)audio drivers for linux
9 #
10 # FFADO is based upon FreeBoB.
11 #
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 2 of the License, or
15 # (at your option) version 3 of the License.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 FFADO_API_VERSION="8"
27 FFADO_VERSION="1.999.30"
28
29 import os
30 import re
31 from string import Template
32 import imp
33
34 build_dir = ARGUMENTS.get('BUILDDIR', "")
35 if build_dir:
36         build_base=build_dir+'/'
37         if not os.path.isdir( build_base ):
38                 os.makedirs( build_base )
39         print "Building into: " + build_base
40 else:
41         build_base=''
42
43 destdir = ARGUMENTS.get( 'DESTDIR', "" )
44
45 if not os.path.isdir( "cache" ):
46         os.makedirs( "cache" )
47
48 opts = Options( "cache/"+build_base+"options.cache" )
49
50 #
51 # If this is just to display a help-text for the variable used via ARGUMENTS, then its wrong...
52 opts.Add( "BUILDDIR", "Path to place the built files in", "")
53
54 opts.AddOptions(
55         BoolOption( "DEBUG", """\
56 Toggle debug-build. DEBUG means \"-g -Wall\" and more, otherwise we will use
57   \"-O2\" to optimise.""", True ),
58         BoolOption( "PROFILE", "Build with symbols and other profiling info", False ),
59         PathOption( "PREFIX", "The prefix where ffado will be installed to.", "/usr/local", PathOption.PathAccept ),
60         PathOption( "BINDIR", "Overwrite the directory where apps are installed to.", "$PREFIX/bin", PathOption.PathAccept ),
61         PathOption( "LIBDIR", "Overwrite the directory where libs are installed to.", "$PREFIX/lib", PathOption.PathAccept ),
62         PathOption( "INCLUDEDIR", "Overwrite the directory where headers are installed to.", "$PREFIX/include", PathOption.PathAccept ),
63         PathOption( "SHAREDIR", "Overwrite the directory where misc shared files are installed to.", "$PREFIX/share/libffado", PathOption.PathAccept ),
64         BoolOption( "ENABLE_BEBOB", "Enable/Disable the bebob part.", True ),
65         BoolOption( "ENABLE_FIREWORKS", "Enable/Disable the ECHO Audio FireWorks AV/C part.", True ),
66         BoolOption( "ENABLE_MOTU", "Enable/Disable the MOTU part.", True ),
67         BoolOption( "ENABLE_GENERICAVC", """\
68 Enable/Disable the the generic avc part (mainly used by apple).
69   Note that disabling this option might be overwritten by other devices needing
70   this code.""", False ),
71         BoolOption( "ENABLE_ALL", "Enable/Disable support for all devices.", False ),
72         BoolOption( "SERIALIZE_USE_EXPAT", "Use libexpat for XML serialization.", False ),
73         BoolOption( "BUILD_TESTS", """\
74 Build the tests in their directory. As some contain quite some functionality,
75   this is on by default.
76   If you just want to use ffado with jack without the tools, you can disable this.\
77 """, True ),
78     BoolOption( "BUILD_STATIC_TOOLS", "Build a statically linked version of the FFADO tools.", False ),
79     EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'powerpc64', 'none' ), ignorecase=2),
80     BoolOption( "ENABLE_OPTIMIZATIONS", "Enable optimizations and the use of processor specific extentions (MMX/SSE/...).", False ),
81
82         )
83
84 ## Load the builders in config
85 buildenv=os.environ
86 vars_to_check = [
87         'PATH',
88         'PKG_CONFIG_PATH',
89         'LD_LIBRARY_PATH',
90         'XDG_CONFIG_DIRS',
91         'XDG_DATA_DIRS',
92         'HOME',
93 ]
94 for var in vars_to_check:
95         if os.environ.has_key(var):
96                 buildenv[var]=os.environ[var]
97         else:
98                 buildenv[var]=''
99
100 env = Environment( tools=['default','scanreplace','pyuic','dbus','doxygen','pkgconfig'], toolpath=['admin'], ENV = buildenv, options=opts )
101
102
103 Help( """
104 For building ffado you can set different options as listed below. You have to
105 specify them only once, scons will save the last value you used and re-use
106 that.
107 To really undo your settings and return to the factory defaults, remove the
108 "cache"-folder and the file ".sconsign.dblite" from this directory.
109 For example with: "rm -Rf .sconsign.dblite cache"
110
111 Note that this is a development version! Don't complain if its not working!
112 See www.ffado.org for stable releases.
113 """ )
114 Help( opts.GenerateHelpText( env ) )
115
116 # make sure the necessary dirs exist
117 if not os.path.isdir( "cache/" + build_base ):
118         os.makedirs( "cache/" + build_base )
119 if not os.path.isdir( 'cache/objects' ):
120         os.makedirs( 'cache/objects' )
121
122 CacheDir( 'cache/objects' )
123
124 opts.Save( 'cache/' + build_base + "options.cache", env )
125
126 def ConfigGuess( context ):
127         context.Message( "Trying to find the system triple: " )
128         ret = os.popen( "admin/config.guess" ).read()[:-1]
129         context.Result( ret )
130         return ret
131
132 def CheckForApp( context, app ):
133         context.Message( "Checking whether '" + app + "' executes " )
134         ret = context.TryAction( app )
135         context.Result( ret[0] )
136         return ret[0]
137
138 def CheckForPyModule( context, module ):
139         context.Message( "Checking for the python module '" + module + "' " )
140         ret = True
141         try:
142                 imp.find_module( module )
143         except ImportError:
144                 ret = False
145         context.Result( ret )
146         return ret
147
148 def CompilerCheck( context ):
149         context.Message( "Checking for a working C-compiler " )
150         ret = context.TryLink( """
151 #include <stdlib.h>
152
153 int main() {
154         printf( "Hello World!" );
155         return 0;
156 }""", '.c' )
157         context.Result( ret )
158         if ret == 0:
159                 return False;
160         context.Message( "Checking for a working C++-compiler " )
161         ret = context.TryLink( """
162 #include <iostream>
163
164 int main() {
165         std::cout << "Hello World!" << std::endl;
166         return 0;
167 }""", ".cpp" )
168         context.Result( ret )
169         return ret
170
171 tests = {
172         "ConfigGuess" : ConfigGuess,
173         "CheckForApp" : CheckForApp,
174         "CheckForPyModule": CheckForPyModule,
175         "CompilerCheck" : CompilerCheck,
176 }
177 tests.update( env['PKGCONFIG_TESTS'] )
178 tests.update( env['PYUIC_TESTS'] )
179
180 conf = Configure( env,
181         custom_tests = tests,
182         conf_dir = "cache/" + build_base,
183         log_file = "cache/" + build_base + 'config.log' )
184
185 if env['SERIALIZE_USE_EXPAT']:
186         env['SERIALIZE_USE_EXPAT']=1
187 else:
188         env['SERIALIZE_USE_EXPAT']=0
189
190 if not env.GetOption('clean'):
191 #       #
192 #       # Check if the environment can actually compile c-files by checking for a
193 #       # header shipped with gcc.
194 #       #
195 #       if not conf.CheckHeader( "stdio.h", language="C" ):
196 #               print "It seems as if stdio.h is missing. This probably means that your build environment is broken, please make sure you have a working c-compiler and libstdc installed and usable."
197 #               Exit( 1 )
198 #       #
199 #       # ... and do the same with a c++-header. Because some distributions have
200 #       # distinct packages for gcc and g++.
201 #       #
202 #       if not conf.CheckHeader( "iostream", language="C++" ):
203 #               print "It seems as if iostream is missing. This probably means that your build environment is broken, please make sure you have a working c++-compiler installed and usable."
204 #               Exit( 1 )
205
206         #
207         # Seems as if the above tests don't really work. This one should do the trick!?
208         #
209         if not conf.CompilerCheck():
210                 print "It seems as if your system isn't even able to compile any C-/C++-programs. Probably you don't have gcc and g++ installed. Compiling a package from source without a working compiler is very hard to do, please install the needed packages (Hint: on *ubuntu you need both gcc- and g++-packages installed)."
211                 Exit( 1 )
212
213         #
214         # The following checks are for headers and libs and packages we need.
215         #
216         allpresent = 1;
217         # for DBUS C++ bindings
218         allpresent &= conf.CheckHeader( "expat.h" )
219         allpresent &= conf.CheckLib( 'expat', 'XML_ExpatVersion', '#include <expat.h>' )
220        
221         allpresent &= conf.CheckForPKGConfig();
222
223         pkgs = {
224                 'libraw1394' : '1.3.0',
225                 'libavc1394' : '0.5.3',
226                 'libiec61883' : '1.1.0',
227                 'dbus-1' : '1.0',
228                 }
229         if not env['SERIALIZE_USE_EXPAT']:
230                 pkgs['libxml++-2.6'] = '2.13.0'
231
232         for pkg in pkgs:
233                 name2 = pkg.replace("+","").replace(".","").replace("-","").upper()
234                 env['%s_FLAGS' % name2] = conf.GetPKGFlags( pkg, pkgs[pkg] )
235                 if env['%s_FLAGS'%name2] == 0:
236                         allpresent &= 0
237
238         if not allpresent:
239                 print """
240 (At least) One of the dependencies is missing. I can't go on without it, please
241 install the needed packages for each of the lines saying "no".
242 (Remember to also install the *-devel packages!)
243
244 And remember to remove the cache with "rm -Rf .sconsign.dblite cache" so the
245 results above get rechecked.
246 """
247                 Exit( 1 )
248
249         # Check for C99 lrint() and lrintf() functions used to convert from
250         # float to integer more efficiently via float_cast.h.  If not
251         # present the standard slower methods will be used instead.  This
252         # might not be the best way of testing for these but it's the only
253         # way which seems to work properly.  CheckFunc() fails due to
254         # argument count problems.
255         if env.has_key( 'CFLAGS' ):
256                 oldcf = env['CFLAGS']
257         else:
258                 oldcf = ""
259         oldcf = env.Append(CFLAGS = '-std=c99')
260         if conf.CheckLibWithHeader( "m", "math.h", "c", "lrint(3.2);" ):
261                 HAVE_LRINT = 1
262         else:
263                 HAVE_LRINT = 0
264         if conf.CheckLibWithHeader( "m", "math.h", "c", "lrintf(3.2);" ):
265                 HAVE_LRINTF = 1
266         else:
267                 HAVE_LRINTF = 0
268         env['HAVE_LRINT'] = HAVE_LRINT;
269         env['HAVE_LRINTF'] = HAVE_LRINTF;
270         env.Replace(CFLAGS=oldcf)
271
272         #
273         # Optional checks follow:
274         #
275
276 if conf.CheckForApp( "which pyuic" ) and conf.CheckForPyModule( 'dbus' ) and conf.CheckForPyModule( 'qt' ):
277         env['PYUIC'] = True
278
279         if conf.CheckForApp( "xdg-desktop-menu --help" ):
280                 env['XDG_TOOLS'] = True
281         else:
282                 print """
283 I couldn't find the program 'xdg-desktop-menu'. Together with xdg-icon-resource
284 this is needed to add the fancy entry to your menu. But the mixer will be installed, you can start it by executing "ffadomixer".
285 """
286
287 else:
288         print """
289 I couldn't find all the prerequisites ('pyuic' and the python-modules 'dbus' and
290 'qt', the packages could be named like dbus-python and PyQt) to build the mixer.
291 Therefor the mixer won't get installed.
292 """
293
294 config_guess = conf.ConfigGuess()
295
296 env = conf.Finish()
297
298 if env['DEBUG']:
299         print "Doing a DEBUG build"
300         # -Werror could be added to, which would force the devs to really remove all the warnings :-)
301         env.AppendUnique( CCFLAGS=["-DDEBUG","-Wall","-g"] )
302         env.AppendUnique( CFLAGS=["-DDEBUG","-Wall","-g"] )
303 else:
304         env.AppendUnique( CCFLAGS=["-O2","-DNDEBUG"] )
305         env.AppendUnique( CFLAGS=["-O2","-DNDEBUG"] )
306
307 if env['PROFILE']:
308         print "Doing a PROFILE build"
309         # -Werror could be added to, which would force the devs to really remove all the warnings :-)
310         env.AppendUnique( CCFLAGS=["-Wall","-g"] )
311         env.AppendUnique( CFLAGS=["-Wall","-g"] )
312
313
314 # this is required to indicate that the DBUS version we use has support
315 # for platform dependent threading init functions
316 # this is true for DBUS >= 0.96 or so. Since we require >= 1.0 it is
317 # always true
318 env.AppendUnique( CCFLAGS=["-DDBUS_HAS_THREADS_INIT_DEFAULT"] )
319
320 if env['ENABLE_ALL']:
321         env['ENABLE_BEBOB'] = True
322         env['ENABLE_FIREWORKS'] = True
323         env['ENABLE_MOTU'] = True
324
325 if env['ENABLE_BEBOB'] or env['ENABLE_FIREWORKS']:
326         env['ENABLE_GENERICAVC'] = True
327
328 env['BUILD_STATIC_LIB'] = False
329 if env['BUILD_STATIC_TOOLS']:
330     print "Building static versions of the tools..."
331     env['BUILD_STATIC_LIB'] = True
332
333 if build_base:
334         env['build_base']="#/"+build_base
335 else:
336         env['build_base']="#/"
337
338 #
339 # Uppercase variables are for usage in code, lowercase versions for usage in
340 # scons-files for installing.
341 #
342 env['BINDIR'] = Template( env['BINDIR'] ).safe_substitute( env )
343 env['LIBDIR'] = Template( env['LIBDIR'] ).safe_substitute( env )
344 env['INCLUDEDIR'] = Template( env['INCLUDEDIR'] ).safe_substitute( env )
345 env['SHAREDIR'] = Template( env['SHAREDIR'] ).safe_substitute( env )
346 env['bindir'] = Template( destdir + env['BINDIR'] ).safe_substitute( env )
347 env['libdir'] = Template( destdir + env['LIBDIR'] ).safe_substitute( env )
348 env['includedir'] = Template( destdir + env['INCLUDEDIR'] ).safe_substitute( env )
349 env['sharedir'] = Template( destdir + env['SHAREDIR'] ).safe_substitute( env )
350
351 env.Command( target=env['sharedir'], source="", action=Mkdir( env['sharedir'] ) )
352
353 env.Alias( "install", env['libdir'] )
354 env.Alias( "install", env['includedir'] )
355 env.Alias( "install", env['sharedir'] )
356 env.Alias( "install", env['bindir'] )
357
358 #
359 # shamelessly copied from the Ardour scons file
360 #
361
362 opt_flags = []
363 env['USE_SSE'] = 0
364
365 # guess at the platform, used to define compiler flags
366
367 config_cpu = 0
368 config_arch = 1
369 config_kernel = 2
370 config_os = 3
371 config = config_guess.split ("-")
372
373 # Autodetect
374 if env['DIST_TARGET'] == 'auto':
375     if re.search ("x86_64", config[config_cpu]) != None:
376         env['DIST_TARGET'] = 'x86_64'
377     elif re.search("i[0-5]86", config[config_cpu]) != None:
378         env['DIST_TARGET'] = 'i386'
379     elif re.search("powerpc64", config[config_cpu]) != None:
380         env['DIST_TARGET'] = 'powerpc64'
381     elif re.search("powerpc", config[config_cpu]) != None:
382         env['DIST_TARGET'] = 'powerpc'
383     else:
384         env['DIST_TARGET'] = 'i686'
385     print "Detected DIST_TARGET = " + env['DIST_TARGET']
386
387 if ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None) or (re.search ("powerpc", config[config_cpu]) != None)):
388    
389     build_host_supports_sse = 0
390     build_host_supports_sse2 = 0
391     build_host_supports_sse3 = 0
392
393     if config[config_kernel] == 'linux' :
394        
395         if (env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64'):
396            
397             flag_line = os.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1]
398             x86_flags = flag_line.split (": ")[1:][0].split ()
399            
400             if "mmx" in x86_flags:
401                 opt_flags.append ("-mmmx")
402             if "sse" in x86_flags:
403                 build_host_supports_sse = 1
404             if "sse2" in x86_flags:
405                 build_host_supports_sse2 = 1
406             #if "sse3" in x86_flags:
407                 #build_host_supports_sse3 = 1
408             if "3dnow" in x86_flags:
409                 opt_flags.append ("-m3dnow")
410            
411             if config[config_cpu] == "i586":
412                 opt_flags.append ("-march=i586")
413             elif config[config_cpu] == "i686":
414                 opt_flags.append ("-march=i686")
415
416         elif (env['DIST_TARGET'] == 'powerpc') or (env['DIST_TARGET'] == 'powerpc64'):
417
418             cpu_line = os.popen ("cat /proc/cpuinfo | grep '^cpu'").read()[:-1]
419
420             ppc_type = cpu_line.split (": ")[1]
421             if re.search ("altivec", ppc_type) != None:
422                 opt_flags.append ("-maltivec")
423                 opt_flags.append ("-mabi=altivec")
424
425             ppc_type = ppc_type.split (", ")[0]
426             if re.match ("74[0145][0578]A?", ppc_type) != None:
427                 opt_flags.append ("-mcpu=7400")
428                 opt_flags.append ("-mtune=7400")
429             elif re.match ("750", ppc_type) != None:
430                 opt_flags.append ("-mcpu=750")
431                 opt_flags.append ("-mtune=750")
432             elif re.match ("PPC970", ppc_type) != None:
433                 opt_flags.append ("-mcpu=970")
434                 opt_flags.append ("-mtune=970")
435             elif re.match ("Cell Broadband Engine", ppc_type) != None:
436                 opt_flags.append ("-mcpu=cell")
437                 opt_flags.append ("-mtune=cell")
438
439     if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) \
440        and build_host_supports_sse and env['ENABLE_OPTIMIZATIONS']:
441         opt_flags.extend (["-msse", "-mfpmath=sse"])
442         env['USE_SSE'] = 1
443
444     if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) \
445        and build_host_supports_sse2 and env['ENABLE_OPTIMIZATIONS']:
446         opt_flags.extend (["-msse2"])
447         env['USE_SSE2'] = 1
448
449     #if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) \
450        #and build_host_supports_sse2 and env['ENABLE_OPTIMIZATIONS']:
451         #opt_flags.extend (["-msse3"])
452         #env['USE_SSE3'] = 1
453
454     # build for 64-bit userland?
455     if env['DIST_TARGET'] == "powerpc64" or env['DIST_TARGET'] == "x86_64":
456         print "Doing a 64-bit build"
457         env.AppendUnique( CCFLAGS=["-m64"] )
458         env.AppendUnique( CFLAGS=["-m64"] )
459     else:
460         print "Doing a 32-bit build"
461         env.AppendUnique( CCFLAGS=["-m32"] )
462         env.AppendUnique( CFLAGS=["-m32"] )
463
464 # end of processor-specific section
465 if env['ENABLE_OPTIMIZATIONS']:
466     opt_flags.extend (["-fomit-frame-pointer","-ffast-math","-funroll-loops"])
467     env.AppendUnique( CCFLAGS=opt_flags )
468     env.AppendUnique( CFLAGS=opt_flags )
469     print "Doing an optimized build..."
470
471 env['REVISION'] = os.popen('svnversion .').read()[:-1]
472 # This may be as simple as '89' or as complex as '4123:4184M'.
473 # We'll just use the last bit.
474 env['REVISION'] = env['REVISION'].split(':')[-1]
475
476 if env['REVISION'] == 'exported':
477         env['REVISION'] = ''
478
479 env['FFADO_API_VERSION']=FFADO_API_VERSION
480
481 env['PACKAGE'] = "libffado"
482 env['VERSION'] = FFADO_VERSION
483 env['LIBVERSION'] = "1.0.0"
484
485 env['CONFIGDIR'] = "~/.ffado"
486 env['CACHEDIR'] = "~/.ffado"
487
488 env['REGISTRATION_URL'] = "http://ffado.org/deviceregistration/register.php?action=register"
489
490 #
491 # To have the top_srcdir as the doxygen-script is used from auto*
492 #
493 env['top_srcdir'] = env.Dir( "." ).abspath
494
495 #
496 # Start building
497 #
498 env.ScanReplace( "config.h.in" )
499 # ensure that the config.h is updated with the version
500
501 env.Depends( "config.h", "SConstruct" )
502 env.Depends( "config.h", 'cache/' + build_base + "options.cache" )
503
504 # update config.h whenever the SVN revision changes
505 env.Depends( "config.h", env.Value(env['REVISION']))
506
507 env.Depends( "libffado.pc", "SConstruct" )
508 pkgconfig = env.ScanReplace( "libffado.pc.in" )
509 env.Install( env['libdir'] + '/pkgconfig', pkgconfig )
510
511 subdirs=['external','src','libffado','tests','support','doc']
512 if build_base:
513         env.SConscript( dirs=subdirs, exports="env", build_dir=build_base+subdir )
514 else:
515         env.SConscript( dirs=subdirs, exports="env" )
516
517 # By default only src is built but all is cleaned
518 if not env.GetOption('clean'):
519     Default( 'src' )
520     Default( 'support' )
521     if env['BUILD_TESTS']:
522         Default( 'tests' )
523
524 #
525 # Deal with the DESTDIR vs. xdg-tools conflict (which is basicely that the
526 # xdg-tools can't deal with DESTDIR, so the packagers have to deal with this
527 # their own :-/
528 #
529 if len(destdir) > 0:
530         if not len( ARGUMENTS.get( "WILL_DEAL_WITH_XDG_MYSELF", "" ) ) > 0:
531                 print """
532 WARNING!
533 You are using the (packagers) option DESTDIR to install this package to a
534 different place than the real prefix. As the xdg-tools can't cope with
535 that, the .desktop-files are not installed by this build, you have to
536 deal with them your own.
537 (And you have to look into the SConstruct to learn how to disable this
538 message.)
539 """
540 else:
541
542         def CleanAction( action ):
543                 if env.GetOption( "clean" ):
544                         env.Execute( action )
545
546         if env.has_key( 'XDG_TOOLS' ) and env.has_key( 'PYUIC' ):
547                 if not env.GetOption("clean"):
548                         action = "install"
549                 else:
550                         action = "uninstall"
551                 mixerdesktopaction = env.Action( "xdg-desktop-menu %s support/xdg/ffado.org-ffadomixer.desktop" % action )
552                 mixericonaction = env.Action( "xdg-icon-resource %s --size 64 --context apps support/xdg/hi64-apps-ffado.png" % action )
553                 env.Command( "__xdgstuff1", None, mixerdesktopaction )
554                 env.Command( "__xdgstuff2", None, mixericonaction )
555                 env.Alias( "install", ["__xdgstuff1", "__xdgstuff2" ] )
556                 CleanAction( mixerdesktopaction )
557                 CleanAction( mixericonaction )
558
559 #
560 # Create a tags-file for easier emacs/vim-source-browsing
561 #  I don't know if the dependency is right...
562 #
563 findcommand = "find . \( -path \"*.h\" -o -path \"*.cpp\" -o -path \"*.c\" \) \! -path \"*.svn*\" \! -path \"./doc*\" \! -path \"./cache*\""
564 env.Command( "tags", "", findcommand + " |xargs ctags" )
565 env.Command( "TAGS", "", findcommand + " |xargs etags" )
566 env.AlwaysBuild( "tags", "TAGS" )
567 env.NoCache( "tags", "TAGS" )
568
Note: See TracBrowser for help on using the browser.