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