38c82b6409c3cc3d50c24b14f4f0c47b994054e8
[senf.git] / site_scons / senfutil.py
1 import os.path, glob, site_tools.Yaptu
2 from SCons.Script import *
3 import senfconf
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 def parseLogOption(value):
15     stream, area, level = ( x.strip() for x in value.strip().split('|') )
16     stream = ''.join('(%s)' % x for x in stream.split('::') )
17     if area : area = ''.join( '(%s)' % x for x in area.split('::') )
18     else    : area = '(_)'
19     return '((%s,%s,%s))' % (stream,area,level)
20
21 def expandLogOption(target, source, env, for_signature):
22     if env.get('LOGLEVELS'):
23         return [ 'SENF_LOG_CONF="' + ''.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )+'"']
24     else:
25         return []
26
27 class BuildTypeOptions:
28     def __init__(self, var):
29         self._var = var
30
31     def __call__(self, target, source, env, for_signature):
32         type = env['final'] and "final" or env['debug'] and "debug" or "normal"
33         return env[self._var + "_" + type]
34
35 def loadTools(env):
36     global senfutildir
37     tooldir = os.path.join(senfutildir, 'site_tools')
38     for tool in os.listdir(tooldir):
39         name, ext = os.path.splitext(tool)
40         if ext == '.py' and name != "__init__" : env.Tool(name, [ tooldir ])
41
42 def parseArguments(env, *defs):
43     vars = Variables(args=ARGUMENTS)
44     for d in defs : vars.Add(d)
45     vars.Update(env)
46     env.Help("""
47 Any construction environment variable may be set from the scons
48 command line (see SConstruct file and SCons documentation for a list
49 of variables) using
50
51    VARNAME=value    Assign new value  
52    VARNAME+=value   Append value at end
53
54 Special command line parameters:
55 """)
56     env.Help(vars.GenerateHelpText(env))
57     try                  : unknv = vars.UnknownVariables()
58     except AttributeError: unknv = vars.UnknownOptions()
59     for k,v in unknv.iteritems():
60         if k.endswith('+'):
61             env.Append(**{k[:-1]: v})
62         else:
63             env.Replace(**{k: v})
64
65
66 ###########################################################################
67 # This looks much more complicated than it is: We do three things here:
68 # a) switch between final or debug options
69 # b) parse the LOGLEVELS parameter into the correct SENF_LOG_CONF syntax
70 # c) check for a local SENF, set options accordingly
71 # d) check, wether the boost extensions are needed
72
73 def SetupForSENF(env, senf_path = []):
74     global senfutildir
75     senf_path.extend(('senf', os.path.dirname(senfutildir), '/usr/local', '/usr'))
76
77     loadTools(env)
78
79     env.Append(
80         LIBS              = [ 'rt' ],
81         
82         CXXFLAGS          = [ '-Wno-long-long', '$CXXFLAGS_' ],
83         CXXFLAGS_         = BuildTypeOptions('CXXFLAGS'),
84         
85         CPPDEFINES        = [ '$expandLogOption', '$CPPDEFINES_' ],
86         expandLogOption   = expandLogOption,
87         CPPDEFINES_       = BuildTypeOptions('CPPDEFINES'),
88         
89         LINKFLAGS         = [ '-rdynamic', '$LINKFLAGS_' ],
90         LINKFLAGS_        = BuildTypeOptions('LINKFLAGS'),
91
92         LOGLEVELS         = [ '$LOGLEVELS_' ],
93         LOGLEVELS_        = BuildTypeOptions('LOGLEVELS'),
94         )
95
96     env.SetDefault( 
97         CXXFLAGS_final    = [],
98         CXXFLAGS_normal   = [],
99         CXXFLAGS_debug    = [],
100
101         CPPDEFINES_final  = [],
102         CPPDEFINES_normal = [],
103         CPPDEFINES_debug  = [],
104
105         LINKFLAGS_final   = [],
106         LINKFLAGS_normal  = [],
107         LINKFLAGS_debug   = [],
108
109         LOGLEVELS_final   = [],
110         LOGLEVELS_normal  = [],
111         LOGLEVELS_debug   = [],
112
113         PROJECTNAME       = "Unnamed project",
114         DOCLINKS          = [],
115         PROJECTEMAIL      = "nobody@nowhere.org",
116         COPYRIGHT         = "nobody",
117         REVISION          = "unknown",
118         )
119
120     # Interpret command line options
121     parseArguments(
122         env, 
123         BoolVariable('final', 'Build final (optimized) build', False),
124         BoolVariable('debug', 'Link in debug symbols', False),
125     )
126
127     # If we have a symbolic link (or directory) 'senf', we use it as our
128     # senf repository
129     for path in senf_path:
130         if not path.startswith('/') : sconspath = '#/%s' % path
131         else                        : sconspath = path
132         if os.path.exists(os.path.join(path,"senf/config.hh")):
133             if not env.GetOption('no_progress'):
134                 print "\nUsing SENF in '%s'\n" \
135                     % ('/..' in sconspath and os.path.abspath(path) or sconspath)
136             env.Append( LIBPATH = [ sconspath ],
137                         CPPPATH = [ sconspath ],
138                         BUNDLEDIR = sconspath,
139                         SENFDIR = sconspath,
140                         SENFSYSLAYOUT = False)
141             try:
142                 env.MergeFlags(file(os.path.join(path,"senf.conf")).read())
143             except IOError:
144                 if not env.GetOption('no_progress'):
145                     print "(SENF configuration file 'senf.conf' not found, assuming non-final SENF)"
146                 env.Append(CPPDEFINES = [ 'SENF_DEBUG' ])
147             break
148         elif os.path.exists(os.path.join(path,"include/senf/config.hh")):
149             if not env.GetOption('no_progress'):
150                 print "\nUsing system SENF in '%s/'\n" % sconspath
151             env.Append(BUNDLEDIR = os.path.join(sconspath,"lib/senf"),
152                        SENFDIR = sconspath,
153                        SENFSYSLAYOUT = True)
154             break
155     else:
156         if not env.GetOption('no_progress'):
157             print "\nSENF library not found .. trying build anyway !!\n"
158
159     Configure(env)
160
161     # Only add senf after all configure checks have run
162     env.Append(
163         CPPPATH = '${NEED_BOOST_EXT and "$SENFDIR/boost_ext" or None}',
164         LIBS = [ 'senf', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
165                  '$BOOSTFSLIB' ],
166         )
167
168     env.Alias('all', '#')
169
170
171 def DefaultOptions(env):
172     env.Append(
173         CXXFLAGS         = [ '-Wall', '-Woverloaded-virtual' ],
174         CXXFLAGS_final   = [ '-O2' ],
175         CXXFLAGS_normal  = [ '-O0', '-g' ],
176         CXXFLAGS_debug   = [ '$CXXFLAGS_normal' ],
177
178         LINKFLAGS_normal = [ '-Wl,-S' ],
179         LINKFLAGS_debug  = [ '-g' ],
180     )
181
182
183 def Glob(env, exclude=[], subdirs=[]):
184     testSources = env.Glob("*.test.cc", strings=True)
185     sources = [ x 
186                 for x in env.Glob("*.cc", strings=True) 
187                 if x not in testSources and x not in exclude ]
188     for subdir in subdirs:
189         testSources += env.Glob(os.path.join(subdir,"*.test.cc"), strings=True)
190         sources += [ x 
191                      for x in env.Glob(os.path.join(subdir,"*.cc"), strings=True)
192                      if x not in testSources and x not in exclude ]
193     sources.sort()
194     testSources.sort()
195     return (sources, testSources)
196
197
198 def Configure(env):
199     conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
200     env.Replace(
201         BOOST_VERSION  =  conf.CheckBoostVersion(),
202         BOOST_VARIANT  = conf.CheckBoostVariants( '', 'mt' ),
203         NEED_BOOST_EXT = not conf.CheckCXXHeader("boost/bimap.hpp"),
204     )
205     conf.Finish()
206
207
208 tagfiles = None
209
210 def Doxygen(env, doxyheader=None, doxyfooter=None, doxycss=None, mydoxyfile=False, senfdoc_path=[],
211             **kw):
212     # Additional interesting keyword arguments or environment variables:
213     #    PROJECTNAME, DOCLINKS, PROJECTEMAIL, COPYRIGHT, REVISION
214
215     global senfutildir
216     global tagfiles
217     libdir=os.path.join(senfutildir, 'lib')
218     
219     if tagfiles is None:
220         senfdocdir = None
221         senfdoc_path.extend(('senfdoc', '$SENFDIR', '$SENFDIR/manual',
222                              '$SENFDIR/share/doc/senf', '$SENFDIR/share/doc/libsenf-doc/html'))
223         for path in senfdoc_path:
224             path = env.Dir(path).get_path()
225             if os.path.exists(os.path.join(path, "doc/doclib.tag")):
226                 senfdocdir = path
227                 break
228         tagfiles = []
229         if senfdocdir is None:
230             if not env.GetOption('no_progress'):
231                 print "(SENF documentation not found)"
232         else:
233             for dir, dirs, files in os.walk(senfdocdir):
234                 tagfiles.extend([ os.path.join(dir,f) for f in files if f.endswith('.tag') ])
235                 if dir.endswith('/doc') : 
236                     try: dirs.remove('html')
237                     except ValueError: pass
238                 for d in dirs: 
239                     if d.startswith('.') : dirs.remove(d)
240     
241     if env.GetOption('clean'):
242         env.Clean('doc', env.Dir('doc'))
243         if not mydoxyfile:
244             env.Clean('doc', "Doxyfile")
245
246     if not mydoxyfile:
247         # Create Doxyfile NOW
248         site_tools.Yaptu.yaptuAction("Doxyfile", 
249                                      os.path.join(libdir, "Doxyfile.yap"),
250                                      env)
251
252     envvalues = [ env.Value('$PROJECTNAME'),
253                   env.Value('$DOCLINKS'),
254                   env.Value('$PROJECTEMAIL'),
255                   env.Value('$COPYRIGHT'),
256                   env.Value('$REVISION') ]
257
258     # The other files are created using dependencies
259     if doxyheader: 
260         doxyheader = env.CopyToDir(env.Dir("doc"), doxyheader)
261     else:
262         doxyheader = env.Yaptu("doc/doxyheader.html", os.path.join(libdir, "doxyheader.yap"), **kw)
263         env.Depends(doxyheader, envvalues)
264     if doxyfooter:
265         doxyfooter = env.CopyToDir(env.Dir("doc"), doxyfooter)
266     else:
267         doxyfooter = env.Yaptu("doc/doxyfooter.html", os.path.join(libdir, "doxyfooter.yap"), **kw)
268         env.Depends(doxyfooter, envvalues)
269     if doxycss:
270         doxycss = env.CopyToDir(env.Dir("doc"), doxycss)
271     else:
272         doxycss    = env.CopyToDir(env.Dir("doc"), os.path.join(libdir, "doxy.css"))
273
274     doc = env.Doxygen("Doxyfile",
275                       DOXYOPTS   = [ '--html', '--tagfiles', '"$TAGFILES"' ],
276                       DOXYENV    = { 'TOPDIR'     : env.Dir('#').abspath,
277                                      'LIBDIR'     : libdir,
278                                      'REVISION'   : '$REVISION',
279                                      'tagfiles'   : '$TAGFILES',
280                                      'output_dir' : 'doc',
281                                      'html_dir'   : 'html',
282                                      'html'       : 'YES',
283                                      'DOXYGEN'    : '$DOXYGEN' },
284                       TAGFILES   = tagfiles, 
285                       DOCLIBDIR  = libdir,
286                       DOXYGENCOM = "$DOCLIBDIR/doxygen.sh $DOXYOPTS $SOURCE")
287
288     env.Depends(doc, [ doxyheader, doxyfooter, doxycss ])
289
290     return doc