Scons: added profile build option
[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         BoolVariable('profile', 'compile and link with the profiling enabled option', False),
209     )
210
211     # Set nice default options
212     env.Append(
213         CXXFLAGS         = [ '-Wall', '-Woverloaded-virtual' ],
214         CXXFLAGS_final   = [ '-O3' ],
215         CXXFLAGS_normal  = [ '-O2', '-g' ],
216         CXXFLAGS_debug   = [ '-O0', '-g' ],
217
218         LINKFLAGS_normal = [ '-Wl,-S' ],
219         LINKFLAGS_debug  = [ '-g' ],
220     )
221     if env['profile']:
222         env.Append(
223             LINKFLAGS   = [ '-pg' ],
224             CXXFLAGS    = [ '-pg' ],
225     )
226
227     env.Alias('all', '#')
228
229
230 def Glob(env, exclude=[], subdirs=[]):
231     testSources = env.Glob("*.test.cc", strings=True)
232     sources = [ x
233                 for x in env.Glob("*.cc", strings=True)
234                 if x not in testSources and x not in exclude ]
235     for subdir in subdirs:
236         testSources += env.Glob(os.path.join(subdir,"*.test.cc"), strings=True)
237         sources += [ x
238                      for x in env.Glob(os.path.join(subdir,"*.cc"), strings=True)
239                      if x not in testSources and x not in exclude ]
240     sources.sort()
241     testSources.sort()
242     return (sources, testSources)
243
244 def CleanGlob(env, targets, patterns):
245     if env.GetOption('clean'):
246         targets = SCons.Util.flatten(targets)
247         for target in targets:
248             if target in BUILD_TARGETS:
249                 patterns = map(str,SCons.Util.flatten(env.subst_list(patterns)))
250                 files = [ os.path.join(path,f)
251                           for path, subdirs, files in os.walk('.')
252                           for pattern in patterns
253                           for f in fnmatch.filter(files,pattern) ]
254                 return env.Clean(target, files)
255
256 tagfiles = None
257
258 def Doxygen(env, doxyheader=None, doxyfooter=None, doxycss=None, mydoxyfile=False, senfdoc_path=[],
259             **kw):
260     # Additional interesting keyword arguments or environment variables:
261     #    PROJECTNAME, DOCLINKS, PROJECTEMAIL, COPYRIGHT, REVISION
262
263     global senfutildir
264     global tagfiles
265     libdir=os.path.join(senfutildir, 'lib')
266
267     if tagfiles is None:
268         senfdocdir = None
269         senfdoc_path.extend(('senfdoc', '$SENFDIR', '$SENFDIR/manual',
270                              '$SENFDIR/share/doc/senf', '$SENFDIR/share/doc/libsenf-doc/html'))
271         for path in senfdoc_path:
272             path = env.Dir(path).get_path()
273             if os.path.exists(os.path.join(path, "doc/doclib.tag")):
274                 senfdocdir = path
275                 break
276         tagfiles = []
277         if senfdocdir is None:
278             if not env.GetOption('no_progress'):
279                 print "(SENF documentation not found)"
280         else:
281             for dir, dirs, files in os.walk(senfdocdir):
282                 tagfiles.extend([ os.path.join(dir,f) for f in files if f.endswith('.tag') ])
283                 if dir.endswith('/doc') :
284                     try: dirs.remove('html')
285                     except ValueError: pass
286                 for d in dirs:
287                     if d.startswith('.') : dirs.remove(d)
288
289     if env.GetOption('clean'):
290         env.Clean('doc', env.Dir('doc'))
291         if not mydoxyfile:
292             env.Clean('doc', "Doxyfile")
293
294     if not mydoxyfile:
295         # Create Doxyfile NOW
296         site_tools.Yaptu.yaptuAction("Doxyfile",
297                                      os.path.join(libdir, "Doxyfile.yap"),
298                                      env)
299
300     envvalues = [ env.Value('$PROJECTNAME'),
301                   env.Value('$DOCLINKS'),
302                   env.Value('$PROJECTEMAIL'),
303                   env.Value('$COPYRIGHT'),
304                   env.Value('$REVISION') ]
305
306     # The other files are created using dependencies
307     if doxyheader:
308         doxyheader = env.CopyToDir(env.Dir("doc"), doxyheader)
309     else:
310         doxyheader = env.Yaptu("doc/doxyheader.html", os.path.join(libdir, "doxyheader.yap"), **kw)
311         env.Depends(doxyheader, envvalues)
312     if doxyfooter:
313         doxyfooter = env.CopyToDir(env.Dir("doc"), doxyfooter)
314     else:
315         doxyfooter = env.Yaptu("doc/doxyfooter.html", os.path.join(libdir, "doxyfooter.yap"), **kw)
316         env.Depends(doxyfooter, envvalues)
317     if doxycss:
318         doxycss = env.CopyToDir(env.Dir("doc"), doxycss)
319     else:
320         doxycss    = env.CopyToDir(env.Dir("doc"), os.path.join(libdir, "doxy.css"))
321
322     doc = env.Doxygen("Doxyfile",
323                       DOXYOPTS   = [ '--html', '--tagfiles', '"$TAGFILES"' ],
324                       DOXYENV    = { 'TOPDIR'     : env.Dir('#').abspath,
325                                      'LIBDIR'     : libdir,
326                                      'REVISION'   : '$REVISION',
327                                      'tagfiles'   : '$TAGFILES',
328                                      'output_dir' : 'doc',
329                                      'html_dir'   : 'html',
330                                      'html'       : 'YES',
331                                      'DOXYGEN'    : '$DOXYGEN' },
332                       TAGFILES   = tagfiles,
333                       DOCLIBDIR  = libdir,
334                       DOXYGENCOM = "$DOCLIBDIR/doxygen.sh $DOXYOPTS $SOURCE")
335
336     env.Depends(doc, [ doxyheader, doxyfooter, doxycss ])
337
338     return doc