Move all 'clean' globbing into senfutil using senfutil.CleanGlob
[senf.git] / site_scons / senfutil.py
1 import os, os.path, site_tools.Yaptu, types, re, fnmatch
2 import SCons.Util
3 from SCons.Script import *
4
5 senfutildir = os.path.dirname(__file__)
6
7 # Fix for SCons 0.97 compatibility
8 try:
9     Variables
10 except NameError:
11     Variables = Options
12     BoolVariable = BoolOption
13
14 ###########################################################################
15
16 def loadTools(env):
17     global senfutildir
18     tooldir = os.path.join(senfutildir, 'site_tools')
19     for tool in os.listdir(tooldir):
20         name, ext = os.path.splitext(tool)
21         if ext == '.py' and name != "__init__" : env.Tool(name, [ tooldir ])
22
23 def parseArguments(env, *defs):
24     vars = Variables(args=ARGUMENTS)
25     for d in defs : vars.Add(d)
26     vars.Update(env)
27     env.Help("""
28 Any construction environment variable may be set from the scons
29 command line (see SConstruct file and SCons documentation for a list
30 of variables) usin
31 g
32
33    VARNAME=value    Assign new value
34    VARNAME+=value   Append value at end
35
36 Special command line parameters:
37 """)
38     env.Help(vars.GenerateHelpText(env))
39     try                  : unknv = vars.UnknownVariables()
40     except AttributeError: unknv = vars.UnknownOptions()
41     env.SetDefault(ARGUMENT_VARIABLES = {})
42     for k,v in ARGLIST:
43         if not unknv.has_key(k) : continue
44         if k.endswith('+'):
45             env.Append(**{k[:-1]: v})
46             env.Append(ARGUMENT_VARIABLES = {k[:-1]:v})
47         else:
48             env.Replace(**{k: v})
49             env.Append(ARGUMENT_VARIABLES = {k:v})
50
51 def importProcessEnv(env):
52     env.Append( ENV = dict(( (k,v)
53                              for pattern in env.get('IMPORT_ENV',[])
54                              for k,v in os.environ.iteritems()
55                              if fnmatch.fnmatchcase(k,pattern) )) )
56
57
58 ###########################################################################$
59 # SENF log option parsing
60
61 def parseLogOption(value):
62     stream, area, level = ( x.strip() for x in value.strip().split('|') )
63     stream = ''.join('(%s)' % x for x in stream.split('::') )
64     if area : area = ''.join( '(%s)' % x for x in area.split('::') )
65     else    : area = '(_)'
66     return '((%s,%s,%s))' % (stream,area,level)
67
68 def expandLogOption(target, source, env, for_signature):
69     if env.subst('$LOGLEVELS'):
70         return [ 'SENF_LOG_CONF="' + ''.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )+'"']
71     else:
72         return []
73
74 ###########################################################################
75 # client SENF detection/configuration
76
77 def detect_senf(env,senf_path, try_flavors):
78     """Detect senf with flavor in 'try_flavors' somewhere in 'senf_path'.
79
80 This function returns True, if senf is found, False otherwise.
81 The environment 'env' is updated in the following way:
82
83     SENFSYSLAYOUT  set to True, if the install is a system installation,
84                    False otherwise
85
86     FLAVOR         set to the detected senf flavor
87
88     SENFDIR        set to the base directory of the senf installation.
89 """
90     global senfutildir
91     senf_path.extend((os.path.dirname(senfutildir), '/usr/local', '/usr'))
92
93     for path in senf_path:
94         if not path.startswith('/') : sconspath = '#/%s' % path
95         else                        : sconspath = path
96         for flavor in try_flavors:
97             suffix = flavor and "_"+flavor or ""
98             local_path = os.path.join(path,"senf%s.conf" % suffix)
99             sys_path = os.path.join(path, "lib", "senf", "senf%s.conf" % suffix)
100             if os.path.exists(local_path):
101                 env.SetDefault( SENFSYSLAYOUT = False )
102             elif os.path.exists(sys_path):
103                 env.SetDefault( SENFSYSLAYOUT  = True )
104             else:
105                 continue
106             env.SetDefault( FLAVOR = flavor, SENFDIR = sconspath )
107             return True
108     return False
109
110 def SetupForSENF(env, senf_path = [], flavor=None):
111     try_flavors = [ '', 'g' ]
112     if flavor is not None:
113         try_flavors[0:0] = [ flavor ]
114
115     res = detect_senf(env, senf_path, try_flavors)
116     if res:
117         if not env.GetOption('no_progress'):
118             print env.subst("scons: Using${SENFSYSLAYOUT and ' system' or ''} "
119                             "'libsenf${LIBADDSUFFIX}' in '$SENFDIR'")
120     else:
121         print "scons: SENF library not found, trying to build anyway ..."
122
123     loadTools(env)
124
125     # For a non-system installed senf, we add library and include search path here
126     if not env.get('SENFSYSLAYOUT', True):
127         env.Append(
128             CPPPATH       = [ '$SENFINCDIR' ],
129             LIBPATH       = [ '$SENFDIR' ],
130             )
131
132     if env['BOOST_VARIANT'] is None:
133         conf = env.Configure(clean=False, help=False)
134         conf.CheckBoostVersion(fail=True)
135         conf.CheckBoostVariants()
136         conf.Finish()
137
138     env.Replace(
139         expandLogOption   = expandLogOption,
140         )
141     env.SetDefault(
142         LIBADDSUFFIX      = '${FLAVOR and "_$FLAVOR" or ""}',
143         OBJADDSUFFIX      = '${LIBADDSUFFIX}',
144         BUNDLEDIR         = '$SENFDIR${SENFSYSLAYOUT and "/lib/senf" or ""}',
145         SENFINCDIR        = '$SENFDIR${SENFSYSLAYOUT and "/include" or ""}',
146
147         PROJECTNAME       = "Unnamed project",
148         DOCLINKS          = [],
149         PROJECTEMAIL      = "nobody@nowhere.org",
150         COPYRIGHT         = "nobody",
151         REVISION          = "unknown",
152         )
153     env.Append(
154         CPPDEFINES        = [ '$expandLogOption' ],
155         CXXFLAGS          = [ '-Wno-long-long', '-fno-strict-aliasing' ],
156         LINKFLAGS         = [ '-rdynamic' ],
157         LIBS              = [ 'senf$LIBADDSUFFIX', 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB',
158                               '$BOOSTSIGNALSLIB', '$BOOSTFSLIB' ],
159         )
160
161     try:
162         path = env.File('$BUNDLEDIR/senf${LIBADDSUFFIX}.conf').abspath
163         env.MergeFlags(file(path).read())
164     except IOError:
165         # Really should never happen since detect_senf looks for this file ...
166         pass
167
168 ###########################################################################
169 # Helpers
170
171 def DefaultOptions(env):
172     env.Replace(
173         expandLogOption   = expandLogOption,
174         CXXFLAGS_         = env.BuildTypeOptions('CXXFLAGS'),
175         CPPDEFINES_       = env.BuildTypeOptions('CPPDEFINES'),
176         LINKFLAGS_        = env.BuildTypeOptions('LINKFLAGS'),
177         LOGLEVELS_        = env.BuildTypeOptions('LOGLEVELS'),
178         )
179     env.Append(
180         CXXFLAGS          = [ '$CXXFLAGS_' ],
181         CPPDEFINES        = [ '$CPPDEFINES_' ],
182         LINKFLAGS         = [ '$LINKFLAGS_' ],
183         LOGLEVELS         = [ '$LOGLEVELS_' ],
184         )
185     env.SetDefault(
186         CXXFLAGS_final    = [],
187         CXXFLAGS_normal   = [],
188         CXXFLAGS_debug    = [],
189
190         CPPDEFINES_final  = [],
191         CPPDEFINES_normal = [],
192         CPPDEFINES_debug  = [],
193
194         LINKFLAGS_final   = [],
195         LINKFLAGS_normal  = [],
196         LINKFLAGS_debug   = [],
197
198         LOGLEVELS_final   = [],
199         LOGLEVELS_normal  = [],
200         LOGLEVELS_debug   = [],
201         )
202
203     # Interpret command line options
204     parseArguments(
205         env,
206         BoolVariable('final', 'Build final (optimized) build', False),
207         BoolVariable('debug', 'Link in debug symbols', False),
208     )
209
210     # Set nice default options
211     env.Append(
212         CXXFLAGS         = [ '-Wall', '-Woverloaded-virtual' ],
213         CXXFLAGS_final   = [ '-O3' ],
214         CXXFLAGS_normal  = [ '-O2', '-g' ],
215         CXXFLAGS_debug   = [ '-O0', '-g' ],
216
217         LINKFLAGS_normal = [ '-Wl,-S' ],
218         LINKFLAGS_debug  = [ '-g' ],
219     )
220
221     env.Alias('all', '#')
222
223
224 def Glob(env, exclude=[], subdirs=[]):
225     testSources = env.Glob("*.test.cc", strings=True)
226     sources = [ x
227                 for x in env.Glob("*.cc", strings=True)
228                 if x not in testSources and x not in exclude ]
229     for subdir in subdirs:
230         testSources += env.Glob(os.path.join(subdir,"*.test.cc"), strings=True)
231         sources += [ x
232                      for x in env.Glob(os.path.join(subdir,"*.cc"), strings=True)
233                      if x not in testSources and x not in exclude ]
234     sources.sort()
235     testSources.sort()
236     return (sources, testSources)
237
238 def CleanGlob(env, targets, patterns):
239     if env.GetOption('clean'):
240         targets = SCons.Util.flatten(targets)
241         for target in targets:
242             if target in BUILD_TARGETS:
243                 patterns = map(str,SCons.Util.flatten(env.subst_list(patterns)))
244                 files = [ os.path.join(path,f)
245                           for path, subdirs, files in os.walk('.')
246                           for pattern in patterns
247                           for f in fnmatch.filter(files,pattern) ]
248                 return env.Clean(target, files)
249
250 tagfiles = None
251
252 def Doxygen(env, doxyheader=None, doxyfooter=None, doxycss=None, mydoxyfile=False, senfdoc_path=[],
253             **kw):
254     # Additional interesting keyword arguments or environment variables:
255     #    PROJECTNAME, DOCLINKS, PROJECTEMAIL, COPYRIGHT, REVISION
256
257     global senfutildir
258     global tagfiles
259     libdir=os.path.join(senfutildir, 'lib')
260
261     if tagfiles is None:
262         senfdocdir = None
263         senfdoc_path.extend(('senfdoc', '$SENFDIR', '$SENFDIR/manual',
264                              '$SENFDIR/share/doc/senf', '$SENFDIR/share/doc/libsenf-doc/html'))
265         for path in senfdoc_path:
266             path = env.Dir(path).get_path()
267             if os.path.exists(os.path.join(path, "doc/doclib.tag")):
268                 senfdocdir = path
269                 break
270         tagfiles = []
271         if senfdocdir is None:
272             if not env.GetOption('no_progress'):
273                 print "(SENF documentation not found)"
274         else:
275             for dir, dirs, files in os.walk(senfdocdir):
276                 tagfiles.extend([ os.path.join(dir,f) for f in files if f.endswith('.tag') ])
277                 if dir.endswith('/doc') :
278                     try: dirs.remove('html')
279                     except ValueError: pass
280                 for d in dirs:
281                     if d.startswith('.') : dirs.remove(d)
282
283     if env.GetOption('clean'):
284         env.Clean('doc', env.Dir('doc'))
285         if not mydoxyfile:
286             env.Clean('doc', "Doxyfile")
287
288     if not mydoxyfile:
289         # Create Doxyfile NOW
290         site_tools.Yaptu.yaptuAction("Doxyfile",
291                                      os.path.join(libdir, "Doxyfile.yap"),
292                                      env)
293
294     envvalues = [ env.Value('$PROJECTNAME'),
295                   env.Value('$DOCLINKS'),
296                   env.Value('$PROJECTEMAIL'),
297                   env.Value('$COPYRIGHT'),
298                   env.Value('$REVISION') ]
299
300     # The other files are created using dependencies
301     if doxyheader:
302         doxyheader = env.CopyToDir(env.Dir("doc"), doxyheader)
303     else:
304         doxyheader = env.Yaptu("doc/doxyheader.html", os.path.join(libdir, "doxyheader.yap"), **kw)
305         env.Depends(doxyheader, envvalues)
306     if doxyfooter:
307         doxyfooter = env.CopyToDir(env.Dir("doc"), doxyfooter)
308     else:
309         doxyfooter = env.Yaptu("doc/doxyfooter.html", os.path.join(libdir, "doxyfooter.yap"), **kw)
310         env.Depends(doxyfooter, envvalues)
311     if doxycss:
312         doxycss = env.CopyToDir(env.Dir("doc"), doxycss)
313     else:
314         doxycss    = env.CopyToDir(env.Dir("doc"), os.path.join(libdir, "doxy.css"))
315
316     doc = env.Doxygen("Doxyfile",
317                       DOXYOPTS   = [ '--html', '--tagfiles', '"$TAGFILES"' ],
318                       DOXYENV    = { 'TOPDIR'     : env.Dir('#').abspath,
319                                      'LIBDIR'     : libdir,
320                                      'REVISION'   : '$REVISION',
321                                      'tagfiles'   : '$TAGFILES',
322                                      'output_dir' : 'doc',
323                                      'html_dir'   : 'html',
324                                      'html'       : 'YES',
325                                      'DOXYGEN'    : '$DOXYGEN' },
326                       TAGFILES   = tagfiles,
327                       DOCLIBDIR  = libdir,
328                       DOXYGENCOM = "$DOCLIBDIR/doxygen.sh $DOXYOPTS $SOURCE")
329
330     env.Depends(doc, [ doxyheader, doxyfooter, doxycss ])
331
332     return doc