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